From 814326e86e929b865020ff44f4576dbdfe3f7ff3 Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Fri, 22 May 2026 15:47:22 -0300 Subject: [PATCH 001/697] drm/xe/hdcp: Add NULL check for media_gt in intel_hdcp_gsc_check_status() commit 60a1e131a811b68703da58fd805ab359b704ab03 upstream. When media GT is disabled via configfs, there is no allocation for media_gt, which is kept as NULL. In such scenario, intel_hdcp_gsc_check_status() results in a kernel pagefault error due to >->uc.gsc being evaluated as an invalid memory address. Fix that by introducing a NULL check on media_gt and bailing out early if so. While at it, also drop the NULL check for gsc, since it can't be NULL if media_gt is not NULL. v2: - Get address for gsc only after checking that gt is not NULL. (Shuicheng) - Drop the NULL check for gsc. (Shuicheng) v3: - Add "Fixes" and "Cc: " tags. (Matt) Fixes: 4af50beb4e0f ("drm/xe: Use gsc_proxy_init_done to check proxy status") Cc: # v6.10+ Reviewed-by: Matt Roper Reviewed-by: Shuicheng Lin Link: https://patch.msgid.link/20260416-check-for-null-media_gt-in-intel_hdcp_gsc_check_status-v2-1-9adb9fd3b621@intel.com Signed-off-by: Gustavo Sousa (cherry picked from commit bfaf87e84ca3ca3f6e275f9ae56da47a8b55ffd1) Signed-off-by: Matthew Brost Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/display/xe_hdcp_gsc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c index 4ae847b628e2..6324f526dcfa 100644 --- a/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c +++ b/drivers/gpu/drm/xe/display/xe_hdcp_gsc.c @@ -35,11 +35,19 @@ bool intel_hdcp_gsc_check_status(struct drm_device *drm) struct xe_device *xe = to_xe_device(drm); struct xe_tile *tile = xe_device_get_root_tile(xe); struct xe_gt *gt = tile->media_gt; - struct xe_gsc *gsc = >->uc.gsc; + struct xe_gsc *gsc; bool ret = true; unsigned int fw_ref; - if (!gsc || !xe_uc_fw_is_enabled(&gsc->fw)) { + if (!gt) { + drm_dbg_kms(&xe->drm, + "not checking GSC status for HDCP2.x: media GT not present or disabled\n"); + return false; + } + + gsc = >->uc.gsc; + + if (!xe_uc_fw_is_enabled(&gsc->fw)) { drm_dbg_kms(&xe->drm, "GSC Components not ready for HDCP2.x\n"); return false; From b9a4184271b9c10144b3490a18f59a756a73542c Mon Sep 17 00:00:00 2001 From: Guanghui Feng Date: Thu, 19 Mar 2026 15:37:54 +0800 Subject: [PATCH 002/697] iommu/amd: Fix illegal cap/mmio access in IOMMU debugfs [ Upstream commit 0e59645683b7b6fa20eceb21a6f420e4f7412943 ] In the current AMD IOMMU debugfs, when multiple processes simultaneously access the IOMMU mmio/cap registers using the IOMMU debugfs, illegal access issues can occur in the following execution flow: 1. CPU1: Sets a valid access address using iommu_mmio/capability_write, and verifies the access address's validity in iommu_mmio/capability_show 2. CPU2: Sets an invalid address using iommu_mmio/capability_write 3. CPU1: accesses the IOMMU mmio/cap registers based on the invalid address, resulting in an illegal access. This patch modifies the execution process to first verify the address's validity and then access it based on the same address, ensuring correctness and robustness. Signed-off-by: Guanghui Feng Signed-off-by: Joerg Roedel Stable-dep-of: 8dfd3d8d7443 ("iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs") Signed-off-by: Sasha Levin --- drivers/iommu/amd/debugfs.c | 42 +++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c index 20b04996441d..0584ca2f859d 100644 --- a/drivers/iommu/amd/debugfs.c +++ b/drivers/iommu/amd/debugfs.c @@ -26,22 +26,19 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf, { struct seq_file *m = filp->private_data; struct amd_iommu *iommu = m->private; - int ret; - - iommu->dbg_mmio_offset = -1; + int ret, dbg_mmio_offset = iommu->dbg_mmio_offset = -1; if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_mmio_offset); + ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset); if (ret) return ret; - if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) { - iommu->dbg_mmio_offset = -1; - return -EINVAL; - } + if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) + return -EINVAL; + iommu->dbg_mmio_offset = dbg_mmio_offset; return cnt; } @@ -49,14 +46,16 @@ static int iommu_mmio_show(struct seq_file *m, void *unused) { struct amd_iommu *iommu = m->private; u64 value; + int dbg_mmio_offset = iommu->dbg_mmio_offset; - if (iommu->dbg_mmio_offset < 0) { + if (dbg_mmio_offset < 0 || dbg_mmio_offset > + iommu->mmio_phys_end - sizeof(u64)) { seq_puts(m, "Please provide mmio register's offset\n"); return 0; } - value = readq(iommu->mmio_base + iommu->dbg_mmio_offset); - seq_printf(m, "Offset:0x%x Value:0x%016llx\n", iommu->dbg_mmio_offset, value); + value = readq(iommu->mmio_base + dbg_mmio_offset); + seq_printf(m, "Offset:0x%x Value:0x%016llx\n", dbg_mmio_offset, value); return 0; } @@ -67,23 +66,20 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf { struct seq_file *m = filp->private_data; struct amd_iommu *iommu = m->private; - int ret; - - iommu->dbg_cap_offset = -1; + int ret, dbg_cap_offset = iommu->dbg_cap_offset = -1; if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &iommu->dbg_cap_offset); + ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset); if (ret) return ret; /* Capability register at offset 0x14 is the last IOMMU capability register. */ - if (iommu->dbg_cap_offset > 0x14) { - iommu->dbg_cap_offset = -1; + if (dbg_cap_offset > 0x14) return -EINVAL; - } + iommu->dbg_cap_offset = dbg_cap_offset; return cnt; } @@ -91,21 +87,21 @@ static int iommu_capability_show(struct seq_file *m, void *unused) { struct amd_iommu *iommu = m->private; u32 value; - int err; + int err, dbg_cap_offset = iommu->dbg_cap_offset; - if (iommu->dbg_cap_offset < 0) { + if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) { seq_puts(m, "Please provide capability register's offset in the range [0x00 - 0x14]\n"); return 0; } - err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + iommu->dbg_cap_offset, &value); + err = pci_read_config_dword(iommu->dev, iommu->cap_ptr + dbg_cap_offset, &value); if (err) { seq_printf(m, "Not able to read capability register at 0x%x\n", - iommu->dbg_cap_offset); + dbg_cap_offset); return 0; } - seq_printf(m, "Offset:0x%x Value:0x%08x\n", iommu->dbg_cap_offset, value); + seq_printf(m, "Offset:0x%x Value:0x%08x\n", dbg_cap_offset, value); return 0; } From 488d2c76bd9f78433a70690d1054bfae3d39a407 Mon Sep 17 00:00:00 2001 From: Eder Zulian Date: Fri, 10 Apr 2026 14:55:50 +0200 Subject: [PATCH 003/697] iommu/amd: Remove latent out-of-bounds access in IOMMU debugfs [ Upstream commit 8dfd3d8d74435344ee8dc9237596959c8b2a6cbe ] In iommu_mmio_write() and iommu_capability_write(), the variables dbg_mmio_offset and dbg_cap_offset are declared as int. However, they are populated using kstrtou32_from_user(). If a user provides a sufficiently large value, it can become a negative integer. Prior to this patch, the AMD IOMMU debugfs implementation was already protected by different mechanisms. 1. #define OFS_IN_SZ 8 ensures the user string <= 8 bytes, so e.g. 0xffffffff isn't a valid input. if (cnt > OFS_IN_SZ) return -EINVAL; 2. Implicit type promotion in iommu_mmio_write(), dbg_mmio_offset is int and iommu->mmio_phys_end is u64 if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) return -EINVAL; 3. The show handlers would currently catch the negative number and refuse to perform the read. Replace kstrtou32_from_user() with kstrtos32_from_user() to parse the input, and check for negative values to explicitly prevent out-of-bounds memory accesses directly in iommu_mmio_write() and iommu_capability_write(). Signed-off-by: Eder Zulian Fixes: 7a4ee419e8c1 ("iommu/amd: Add debugfs support to dump IOMMU MMIO registers") Cc: stable@vger.kernel.org Signed-off-by: Joerg Roedel Signed-off-by: Sasha Levin --- drivers/iommu/amd/debugfs.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c index 0584ca2f859d..3909a1fb218e 100644 --- a/drivers/iommu/amd/debugfs.c +++ b/drivers/iommu/amd/debugfs.c @@ -31,11 +31,12 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf, if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_mmio_offset); + ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_mmio_offset); if (ret) return ret; - if (dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) + if (dbg_mmio_offset < 0 || dbg_mmio_offset > + iommu->mmio_phys_end - sizeof(u64)) return -EINVAL; iommu->dbg_mmio_offset = dbg_mmio_offset; @@ -71,12 +72,12 @@ static ssize_t iommu_capability_write(struct file *filp, const char __user *ubuf if (cnt > OFS_IN_SZ) return -EINVAL; - ret = kstrtou32_from_user(ubuf, cnt, 0, &dbg_cap_offset); + ret = kstrtos32_from_user(ubuf, cnt, 0, &dbg_cap_offset); if (ret) return ret; /* Capability register at offset 0x14 is the last IOMMU capability register. */ - if (dbg_cap_offset > 0x14) + if (dbg_cap_offset < 0 || dbg_cap_offset > 0x14) return -EINVAL; iommu->dbg_cap_offset = dbg_cap_offset; From da3d241c5b925f17a9d8051d7a9e0d454d8e01f6 Mon Sep 17 00:00:00 2001 From: Luis Henriques Date: Mon, 16 Feb 2026 14:48:30 +0000 Subject: [PATCH 004/697] fuse: fix uninit-value in fuse_dentry_revalidate() commit 5a6baf204610589f8a5b5a1cd69d1fe661d9d3cd upstream. fuse_dentry_revalidate() may be called with a dentry that didn't had ->d_time initialised. The issue was found with KMSAN, where lookup_open() calls __d_alloc(), followed by d_revalidate(), as shown below: ===================================================== BUG: KMSAN: uninit-value in fuse_dentry_revalidate+0x150/0x13d0 fs/fuse/dir.c:394 fuse_dentry_revalidate+0x150/0x13d0 fs/fuse/dir.c:394 d_revalidate fs/namei.c:1030 [inline] lookup_open fs/namei.c:4405 [inline] open_last_lookups fs/namei.c:4583 [inline] path_openat+0x1614/0x64c0 fs/namei.c:4827 do_file_open+0x2aa/0x680 fs/namei.c:4859 [...] Uninit was created at: slab_post_alloc_hook mm/slub.c:4466 [inline] slab_alloc_node mm/slub.c:4788 [inline] kmem_cache_alloc_lru_noprof+0x382/0x1280 mm/slub.c:4807 __d_alloc+0x55/0xa00 fs/dcache.c:1740 d_alloc_parallel+0x99/0x2740 fs/dcache.c:2604 lookup_open fs/namei.c:4398 [inline] open_last_lookups fs/namei.c:4583 [inline] path_openat+0x135f/0x64c0 fs/namei.c:4827 do_file_open+0x2aa/0x680 fs/namei.c:4859 [...] ===================================================== Reported-by: syzbot+fdebb2dc960aa56c600a@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69917e0d.050a0220.340abe.02e2.GAE@google.com Fixes: 2396356a945b ("fuse: add more control over cache invalidation behaviour") Signed-off-by: Luis Henriques Signed-off-by: Miklos Szeredi Signed-off-by: Sasha Levin --- fs/fuse/dir.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index ecaec0fea3a1..1bc6982b5d6a 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -283,21 +283,33 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, goto out; } -#if BITS_PER_LONG < 64 static int fuse_dentry_init(struct dentry *dentry) { + int ret = 0; + + /* + * Initialising d_time (epoch) to '0' ensures the dentry is invalid + * if compared to fc->epoch, which is initialized to '1'. + */ + dentry->d_time = 0; + +#if BITS_PER_LONG < 64 dentry->d_fsdata = kzalloc(sizeof(union fuse_dentry), GFP_KERNEL_ACCOUNT | __GFP_RECLAIMABLE); - return dentry->d_fsdata ? 0 : -ENOMEM; + ret = dentry->d_fsdata ? 0 : -ENOMEM; +#endif + + return ret; } static void fuse_dentry_release(struct dentry *dentry) { +#if BITS_PER_LONG < 64 union fuse_dentry *fd = dentry->d_fsdata; kfree_rcu(fd, rcu); -} #endif +} static int fuse_dentry_delete(const struct dentry *dentry) { @@ -331,10 +343,8 @@ static struct vfsmount *fuse_dentry_automount(struct path *path) const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, .d_delete = fuse_dentry_delete, -#if BITS_PER_LONG < 64 .d_init = fuse_dentry_init, .d_release = fuse_dentry_release, -#endif .d_automount = fuse_dentry_automount, }; From dc184ac2f0ba77ae19725ee06ad3ab36bb9d1f61 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Thu, 19 Feb 2026 16:16:17 -0800 Subject: [PATCH 005/697] cxl/mbox: validate payload size before accessing contents in cxl_payload_from_user_allowed() [ Upstream commit 60b5d1f68338aff2c5af0113f04aefa7169c50c2 ] cxl_payload_from_user_allowed() casts and dereferences the input payload without first verifying its size. When a raw mailbox command is sent with an undersized payload (ie: 1 byte for CXL_MBOX_OP_CLEAR_LOG, which expects a 16-byte UUID), uuid_equal() reads past the allocated buffer, triggering a KASAN splat: BUG: KASAN: slab-out-of-bounds in memcmp+0x176/0x1d0 lib/string.c:683 Read of size 8 at addr ffff88810130f5c0 by task syz.1.62/2258 CPU: 2 UID: 0 PID: 2258 Comm: syz.1.62 Not tainted 6.19.0-dirty #3 PREEMPT(voluntary) Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xab/0xe0 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xce/0x650 mm/kasan/report.c:482 kasan_report+0xce/0x100 mm/kasan/report.c:595 memcmp+0x176/0x1d0 lib/string.c:683 uuid_equal include/linux/uuid.h:73 [inline] cxl_payload_from_user_allowed drivers/cxl/core/mbox.c:345 [inline] cxl_mbox_cmd_ctor drivers/cxl/core/mbox.c:368 [inline] cxl_validate_cmd_from_user drivers/cxl/core/mbox.c:522 [inline] cxl_send_cmd+0x9c0/0xb50 drivers/cxl/core/mbox.c:643 __cxl_memdev_ioctl drivers/cxl/core/memdev.c:698 [inline] cxl_memdev_ioctl+0x14f/0x190 drivers/cxl/core/memdev.c:713 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:597 [inline] __se_sys_ioctl fs/ioctl.c:583 [inline] __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0xa8/0x330 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fdaf331ba79 Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fdaf1d77038 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 00007fdaf3585fa0 RCX: 00007fdaf331ba79 RDX: 00002000000001c0 RSI: 00000000c030ce02 RDI: 0000000000000003 RBP: 00007fdaf33749df R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 00007fdaf3586038 R14: 00007fdaf3585fa0 R15: 00007ffced2af768 Add 'in_size' parameter to cxl_payload_from_user_allowed() and validate the payload is large enough. Fixes: 6179045ccc0c ("cxl/mbox: Block immediate mode in SET_PARTITION_INFO command") Fixes: 206f9fa9d555 ("cxl/mbox: Add Clear Log mailbox command") Signed-off-by: Davidlohr Bueso Reviewed-by: Alison Schofield Reviewed-by: Dave Jiang Link: https://patch.msgid.link/20260220001618.963490-2-dave@stgolabs.net Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- drivers/cxl/core/mbox.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c index fa6dd0c94656..e7a6452bf544 100644 --- a/drivers/cxl/core/mbox.c +++ b/drivers/cxl/core/mbox.c @@ -311,6 +311,7 @@ static bool cxl_mem_raw_command_allowed(u16 opcode) * cxl_payload_from_user_allowed() - Check contents of in_payload. * @opcode: The mailbox command opcode. * @payload_in: Pointer to the input payload passed in from user space. + * @in_size: Size of @payload_in in bytes. * * Return: * * true - payload_in passes check for @opcode. @@ -325,12 +326,15 @@ static bool cxl_mem_raw_command_allowed(u16 opcode) * * The specific checks are determined by the opcode. */ -static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in) +static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in, + size_t in_size) { switch (opcode) { case CXL_MBOX_OP_SET_PARTITION_INFO: { struct cxl_mbox_set_partition_info *pi = payload_in; + if (in_size < sizeof(*pi)) + return false; if (pi->flags & CXL_SET_PARTITION_IMMEDIATE_FLAG) return false; break; @@ -338,6 +342,8 @@ static bool cxl_payload_from_user_allowed(u16 opcode, void *payload_in) case CXL_MBOX_OP_CLEAR_LOG: { const uuid_t *uuid = (uuid_t *)payload_in; + if (in_size < sizeof(uuid_t)) + return false; /* * Restrict the ‘Clear log’ action to only apply to * Vendor debug logs. @@ -365,7 +371,8 @@ static int cxl_mbox_cmd_ctor(struct cxl_mbox_cmd *mbox_cmd, if (IS_ERR(mbox_cmd->payload_in)) return PTR_ERR(mbox_cmd->payload_in); - if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in)) { + if (!cxl_payload_from_user_allowed(opcode, mbox_cmd->payload_in, + in_size)) { dev_dbg(cxl_mbox->host, "%s: input payload not allowed\n", cxl_mem_opcode_to_name(opcode)); kvfree(mbox_cmd->payload_in); From 3f0543bdf446a62b7293c445377a1e5741817dfd Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2024 13:43:43 +0100 Subject: [PATCH 006/697] sched: Employ sched_change guards [ Upstream commit e9139f765ac7048cadc9981e962acdf8b08eabf3 ] As proposed a long while ago -- and half done by scx -- wrap the scheduler's 'change' pattern in a guard helper. Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Juri Lelli Acked-by: Tejun Heo Acked-by: Vincent Guittot Stable-dep-of: d658686a1331 ("sched/deadline: Fix missing ENQUEUE_REPLENISH during PI de-boosting") Signed-off-by: Sasha Levin --- include/linux/cleanup.h | 5 ++ kernel/sched/core.c | 159 +++++++++++++++------------------------- kernel/sched/ext.c | 39 +++++----- kernel/sched/sched.h | 33 ++++++--- kernel/sched/syscalls.c | 65 ++++++---------- 5 files changed, 131 insertions(+), 170 deletions(-) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index 19c7e475d3a4..a1194e44b527 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -341,6 +341,11 @@ _label: \ #define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond) \ static __maybe_unused const bool class_##_name##_is_conditional = _is_cond +#define DEFINE_CLASS_IS_UNCONDITIONAL(_name) \ + __DEFINE_CLASS_IS_CONDITIONAL(_name, false); \ + static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \ + { return (void *)1; } + #define __GUARD_IS_ERR(_ptr) \ ({ \ unsigned long _rc = (__force unsigned long)(_ptr); \ diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 0d93f60fed20..46fc94f2338e 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -7332,7 +7332,7 @@ void rt_mutex_post_schedule(void) */ void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task) { - int prio, oldprio, queued, running, queue_flag = + int prio, oldprio, queue_flag = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK; const struct sched_class *prev_class, *next_class; struct rq_flags rf; @@ -7397,52 +7397,42 @@ void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task) if (prev_class != next_class && p->se.sched_delayed) dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); - queued = task_on_rq_queued(p); - running = task_current_donor(rq, p); - if (queued) - dequeue_task(rq, p, queue_flag); - if (running) - put_prev_task(rq, p); - - /* - * Boosting condition are: - * 1. -rt task is running and holds mutex A - * --> -dl task blocks on mutex A - * - * 2. -dl task is running and holds mutex A - * --> -dl task blocks on mutex A and could preempt the - * running task - */ - if (dl_prio(prio)) { - if (!dl_prio(p->normal_prio) || - (pi_task && dl_prio(pi_task->prio) && - dl_entity_preempt(&pi_task->dl, &p->dl))) { - p->dl.pi_se = pi_task->dl.pi_se; - queue_flag |= ENQUEUE_REPLENISH; + scoped_guard (sched_change, p, queue_flag) { + /* + * Boosting condition are: + * 1. -rt task is running and holds mutex A + * --> -dl task blocks on mutex A + * + * 2. -dl task is running and holds mutex A + * --> -dl task blocks on mutex A and could preempt the + * running task + */ + if (dl_prio(prio)) { + if (!dl_prio(p->normal_prio) || + (pi_task && dl_prio(pi_task->prio) && + dl_entity_preempt(&pi_task->dl, &p->dl))) { + p->dl.pi_se = pi_task->dl.pi_se; + scope->flags |= ENQUEUE_REPLENISH; + } else { + p->dl.pi_se = &p->dl; + } + } else if (rt_prio(prio)) { + if (dl_prio(oldprio)) + p->dl.pi_se = &p->dl; + if (oldprio < prio) + scope->flags |= ENQUEUE_HEAD; } else { - p->dl.pi_se = &p->dl; + if (dl_prio(oldprio)) + p->dl.pi_se = &p->dl; + if (rt_prio(oldprio)) + p->rt.timeout = 0; } - } else if (rt_prio(prio)) { - if (dl_prio(oldprio)) - p->dl.pi_se = &p->dl; - if (oldprio < prio) - queue_flag |= ENQUEUE_HEAD; - } else { - if (dl_prio(oldprio)) - p->dl.pi_se = &p->dl; - if (rt_prio(oldprio)) - p->rt.timeout = 0; - } - p->sched_class = next_class; - p->prio = prio; + p->sched_class = next_class; + p->prio = prio; - check_class_changing(rq, p, prev_class); - - if (queued) - enqueue_task(rq, p, queue_flag); - if (running) - set_next_task(rq, p); + check_class_changing(rq, p, prev_class); + } check_class_changed(rq, p, prev_class, oldprio); out_unlock: @@ -8090,26 +8080,9 @@ int migrate_task_to(struct task_struct *p, int target_cpu) */ void sched_setnuma(struct task_struct *p, int nid) { - bool queued, running; - struct rq_flags rf; - struct rq *rq; - - rq = task_rq_lock(p, &rf); - queued = task_on_rq_queued(p); - running = task_current_donor(rq, p); - - if (queued) - dequeue_task(rq, p, DEQUEUE_SAVE); - if (running) - put_prev_task(rq, p); - - p->numa_preferred_nid = nid; - - if (queued) - enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK); - if (running) - set_next_task(rq, p); - task_rq_unlock(rq, p, &rf); + guard(task_rq_lock)(p); + scoped_guard (sched_change, p, DEQUEUE_SAVE) + p->numa_preferred_nid = nid; } #endif /* CONFIG_NUMA_BALANCING */ @@ -9215,8 +9188,9 @@ static void sched_change_group(struct task_struct *tsk) */ void sched_move_task(struct task_struct *tsk, bool for_autogroup) { - int queued, running, queue_flags = + unsigned int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK; + bool resched = false; struct rq *rq; CLASS(task_rq_lock, rq_guard)(tsk); @@ -9224,29 +9198,16 @@ void sched_move_task(struct task_struct *tsk, bool for_autogroup) update_rq_clock(rq); - running = task_current_donor(rq, tsk); - queued = task_on_rq_queued(tsk); - - if (queued) - dequeue_task(rq, tsk, queue_flags); - if (running) - put_prev_task(rq, tsk); - - sched_change_group(tsk); - if (!for_autogroup) - scx_cgroup_move_task(tsk); + scoped_guard (sched_change, tsk, queue_flags) { + sched_change_group(tsk); + if (!for_autogroup) + scx_cgroup_move_task(tsk); + if (scope->running) + resched = true; + } - if (queued) - enqueue_task(rq, tsk, queue_flags); - if (running) { - set_next_task(rq, tsk); - /* - * After changing group, the running task may have joined a - * throttled one but it's still the running task. Trigger a - * resched to make sure that task can still run. - */ + if (resched) resched_curr(rq); - } } static struct cgroup_subsys_state * @@ -10902,37 +10863,39 @@ void sched_mm_cid_fork(struct task_struct *t) } #endif /* CONFIG_SCHED_MM_CID */ -#ifdef CONFIG_SCHED_CLASS_EXT -void sched_deq_and_put_task(struct task_struct *p, int queue_flags, - struct sched_enq_and_set_ctx *ctx) +static DEFINE_PER_CPU(struct sched_change_ctx, sched_change_ctx); + +struct sched_change_ctx *sched_change_begin(struct task_struct *p, unsigned int flags) { + struct sched_change_ctx *ctx = this_cpu_ptr(&sched_change_ctx); struct rq *rq = task_rq(p); lockdep_assert_rq_held(rq); - *ctx = (struct sched_enq_and_set_ctx){ + *ctx = (struct sched_change_ctx){ .p = p, - .queue_flags = queue_flags, + .flags = flags, .queued = task_on_rq_queued(p), - .running = task_current(rq, p), + .running = task_current_donor(rq, p), }; - update_rq_clock(rq); if (ctx->queued) - dequeue_task(rq, p, queue_flags | DEQUEUE_NOCLOCK); + dequeue_task(rq, p, flags); if (ctx->running) put_prev_task(rq, p); + + return ctx; } -void sched_enq_and_set_task(struct sched_enq_and_set_ctx *ctx) +void sched_change_end(struct sched_change_ctx *ctx) { - struct rq *rq = task_rq(ctx->p); + struct task_struct *p = ctx->p; + struct rq *rq = task_rq(p); lockdep_assert_rq_held(rq); if (ctx->queued) - enqueue_task(rq, ctx->p, ctx->queue_flags | ENQUEUE_NOCLOCK); + enqueue_task(rq, p, ctx->flags | ENQUEUE_NOCLOCK); if (ctx->running) - set_next_task(rq, ctx->p); + set_next_task(rq, p); } -#endif /* CONFIG_SCHED_CLASS_EXT */ diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 35c0b31924d3..3029e5b8f9a5 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -3866,11 +3866,10 @@ static void scx_bypass(bool bypass) */ list_for_each_entry_safe_reverse(p, n, &rq->scx.runnable_list, scx.runnable_node) { - struct sched_enq_and_set_ctx ctx; - /* cycling deq/enq is enough, see the function comment */ - sched_deq_and_put_task(p, DEQUEUE_SAVE | DEQUEUE_MOVE, &ctx); - sched_enq_and_set_task(&ctx); + scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_MOVE) { + /* nothing */ ; + } } /* resched to restore ticks and idle state */ @@ -4021,17 +4020,16 @@ static void scx_disable_workfn(struct kthread_work *work) while ((p = scx_task_iter_next_locked(&sti))) { const struct sched_class *old_class = p->sched_class; const struct sched_class *new_class = scx_setscheduler_class(p); - struct sched_enq_and_set_ctx ctx; - if (old_class != new_class && p->se.sched_delayed) - dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED); + update_rq_clock(task_rq(p)); - sched_deq_and_put_task(p, DEQUEUE_SAVE | DEQUEUE_MOVE, &ctx); - - p->sched_class = new_class; - check_class_changing(task_rq(p), p, old_class); + if (old_class != new_class && p->se.sched_delayed) + dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); - sched_enq_and_set_task(&ctx); + scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK) { + p->sched_class = new_class; + check_class_changing(task_rq(p), p, old_class); + } check_class_changed(task_rq(p), p, old_class, p->prio); scx_exit_task(p); @@ -4845,21 +4843,20 @@ static void scx_enable_workfn(struct kthread_work *work) while ((p = scx_task_iter_next_locked(&sti))) { const struct sched_class *old_class = p->sched_class; const struct sched_class *new_class = scx_setscheduler_class(p); - struct sched_enq_and_set_ctx ctx; if (!tryget_task_struct(p)) continue; - if (old_class != new_class && p->se.sched_delayed) - dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED); - - sched_deq_and_put_task(p, DEQUEUE_SAVE | DEQUEUE_MOVE, &ctx); + update_rq_clock(task_rq(p)); - p->scx.slice = SCX_SLICE_DFL; - p->sched_class = new_class; - check_class_changing(task_rq(p), p, old_class); + if (old_class != new_class && p->se.sched_delayed) + dequeue_task(task_rq(p), p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); - sched_enq_and_set_task(&ctx); + scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK) { + p->scx.slice = SCX_SLICE_DFL; + p->sched_class = new_class; + check_class_changing(task_rq(p), p, old_class); + } check_class_changed(task_rq(p), p, old_class, p->prio); put_task_struct(p); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index f750dea7b787..668841022dbf 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -3891,23 +3891,38 @@ extern void check_class_changed(struct rq *rq, struct task_struct *p, extern struct balance_callback *splice_balance_callbacks(struct rq *rq); extern void balance_callbacks(struct rq *rq, struct balance_callback *head); -#ifdef CONFIG_SCHED_CLASS_EXT /* - * Used by SCX in the enable/disable paths to move tasks between sched_classes - * and establish invariants. + * The 'sched_change' pattern is the safe, easy and slow way of changing a + * task's scheduling properties. It dequeues a task, such that the scheduler + * is fully unaware of it; at which point its properties can be modified; + * after which it is enqueued again. + * + * Typically this must be called while holding task_rq_lock, since most/all + * properties are serialized under those locks. There is currently one + * exception to this rule in sched/ext which only holds rq->lock. + */ + +/* + * This structure is a temporary, used to preserve/convey the queueing state + * of the task between sched_change_begin() and sched_change_end(). Ensuring + * the task's queueing state is idempotent across the operation. */ -struct sched_enq_and_set_ctx { +struct sched_change_ctx { struct task_struct *p; - int queue_flags; + int flags; bool queued; bool running; }; -void sched_deq_and_put_task(struct task_struct *p, int queue_flags, - struct sched_enq_and_set_ctx *ctx); -void sched_enq_and_set_task(struct sched_enq_and_set_ctx *ctx); +struct sched_change_ctx *sched_change_begin(struct task_struct *p, unsigned int flags); +void sched_change_end(struct sched_change_ctx *ctx); -#endif /* CONFIG_SCHED_CLASS_EXT */ +DEFINE_CLASS(sched_change, struct sched_change_ctx *, + sched_change_end(_T), + sched_change_begin(p, flags), + struct task_struct *p, unsigned int flags) + +DEFINE_CLASS_IS_UNCONDITIONAL(sched_change) #include "ext.h" diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c index 6805a63d47af..d2bcedc10152 100644 --- a/kernel/sched/syscalls.c +++ b/kernel/sched/syscalls.c @@ -64,7 +64,6 @@ static int effective_prio(struct task_struct *p) void set_user_nice(struct task_struct *p, long nice) { - bool queued, running; struct rq *rq; int old_prio; @@ -90,22 +89,12 @@ void set_user_nice(struct task_struct *p, long nice) return; } - queued = task_on_rq_queued(p); - running = task_current_donor(rq, p); - if (queued) - dequeue_task(rq, p, DEQUEUE_SAVE | DEQUEUE_NOCLOCK); - if (running) - put_prev_task(rq, p); - - p->static_prio = NICE_TO_PRIO(nice); - set_load_weight(p, true); - old_prio = p->prio; - p->prio = effective_prio(p); - - if (queued) - enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK); - if (running) - set_next_task(rq, p); + scoped_guard (sched_change, p, DEQUEUE_SAVE | DEQUEUE_NOCLOCK) { + p->static_prio = NICE_TO_PRIO(nice); + set_load_weight(p, true); + old_prio = p->prio; + p->prio = effective_prio(p); + } /* * If the task increased its priority or is running and @@ -515,7 +504,7 @@ int __sched_setscheduler(struct task_struct *p, bool user, bool pi) { int oldpolicy = -1, policy = attr->sched_policy; - int retval, oldprio, newprio, queued, running; + int retval, oldprio, newprio; const struct sched_class *prev_class, *next_class; struct balance_callback *head; struct rq_flags rf; @@ -698,33 +687,25 @@ int __sched_setscheduler(struct task_struct *p, if (prev_class != next_class && p->se.sched_delayed) dequeue_task(rq, p, DEQUEUE_SLEEP | DEQUEUE_DELAYED | DEQUEUE_NOCLOCK); - queued = task_on_rq_queued(p); - running = task_current_donor(rq, p); - if (queued) - dequeue_task(rq, p, queue_flags); - if (running) - put_prev_task(rq, p); - - if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS)) { - __setscheduler_params(p, attr); - p->sched_class = next_class; - p->prio = newprio; - } - __setscheduler_uclamp(p, attr); - check_class_changing(rq, p, prev_class); + scoped_guard (sched_change, p, queue_flags) { - if (queued) { - /* - * We enqueue to tail when the priority of a task is - * increased (user space view). - */ - if (oldprio < p->prio) - queue_flags |= ENQUEUE_HEAD; + if (!(attr->sched_flags & SCHED_FLAG_KEEP_PARAMS)) { + __setscheduler_params(p, attr); + p->sched_class = next_class; + p->prio = newprio; + } + __setscheduler_uclamp(p, attr); + check_class_changing(rq, p, prev_class); - enqueue_task(rq, p, queue_flags); + if (scope->queued) { + /* + * We enqueue to tail when the priority of a task is + * increased (user space view). + */ + if (oldprio < p->prio) + scope->flags |= ENQUEUE_HEAD; + } } - if (running) - set_next_task(rq, p); check_class_changed(rq, p, prev_class, oldprio); From 0638bf16b7a73a2fe63624bd0d16d9fd904805c3 Mon Sep 17 00:00:00 2001 From: Juri Lelli Date: Mon, 2 Mar 2026 16:45:40 +0100 Subject: [PATCH 007/697] sched/deadline: Fix missing ENQUEUE_REPLENISH during PI de-boosting [ Upstream commit d658686a1331db3bb108ca079d76deb3208ed949 ] Running stress-ng --schedpolicy 0 on an RT kernel on a big machine might lead to the following WARNINGs (edited). sched: DL de-boosted task PID 22725: REPLENISH flag missing WARNING: CPU: 93 PID: 0 at kernel/sched/deadline.c:239 dequeue_task_dl+0x15c/0x1f8 ... (running_bw underflow) Call trace: dequeue_task_dl+0x15c/0x1f8 (P) dequeue_task+0x80/0x168 deactivate_task+0x24/0x50 push_dl_task+0x264/0x2e0 dl_task_timer+0x1b0/0x228 __hrtimer_run_queues+0x188/0x378 hrtimer_interrupt+0xfc/0x260 ... The problem is that when a SCHED_DEADLINE task (lock holder) is changed to a lower priority class via sched_setscheduler(), it may fail to properly inherit the parameters of potential DEADLINE donors if it didn't already inherit them in the past (shorter deadline than donor's at that time). This might lead to bandwidth accounting corruption, as enqueue_task_dl() won't recognize the lock holder as boosted. The scenario occurs when: 1. A DEADLINE task (donor) blocks on a PI mutex held by another DEADLINE task (holder), but the holder doesn't inherit parameters (e.g., it already has a shorter deadline) 2. sched_setscheduler() changes the holder from DEADLINE to a lower class while still holding the mutex 3. The holder should now inherit DEADLINE parameters from the donor and be enqueued with ENQUEUE_REPLENISH, but this doesn't happen Fix the issue by introducing __setscheduler_dl_pi(), which detects when a DEADLINE (proper or boosted) task gets setscheduled to a lower priority class. In case, the function makes the task inherit DEADLINE parameters of the donoer (pi_se) and sets ENQUEUE_REPLENISH flag to ensure proper bandwidth accounting during the next enqueue operation. Fixes: 2279f540ea7d ("sched/deadline: Fix priority inheritance with multiple scheduling classes") Reported-by: Bruno Goncalves Signed-off-by: Juri Lelli Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/20260302-upstream-fix-deadline-piboost-b4-v3-1-6ba32184a9e0@redhat.com Signed-off-by: Sasha Levin --- kernel/sched/syscalls.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/kernel/sched/syscalls.c b/kernel/sched/syscalls.c index d2bcedc10152..77b663a5dfb2 100644 --- a/kernel/sched/syscalls.c +++ b/kernel/sched/syscalls.c @@ -322,6 +322,35 @@ static bool check_same_owner(struct task_struct *p) uid_eq(cred->euid, pcred->uid)); } +#ifdef CONFIG_RT_MUTEXES +static inline void __setscheduler_dl_pi(int newprio, int policy, + struct task_struct *p, + struct sched_change_ctx *scope) +{ + /* + * In case a DEADLINE task (either proper or boosted) gets + * setscheduled to a lower priority class, check if it neeeds to + * inherit parameters from a potential pi_task. In that case make + * sure replenishment happens with the next enqueue. + */ + + if (dl_prio(newprio) && !dl_policy(policy)) { + struct task_struct *pi_task = rt_mutex_get_top_task(p); + + if (pi_task) { + p->dl.pi_se = pi_task->dl.pi_se; + scope->flags |= ENQUEUE_REPLENISH; + } + } +} +#else /* !CONFIG_RT_MUTEXES */ +static inline void __setscheduler_dl_pi(int newprio, int policy, + struct task_struct *p, + struct sched_change_ctx *scope) +{ +} +#endif /* !CONFIG_RT_MUTEXES */ + #ifdef CONFIG_UCLAMP_TASK static int uclamp_validate(struct task_struct *p, @@ -693,6 +722,7 @@ int __sched_setscheduler(struct task_struct *p, __setscheduler_params(p, attr); p->sched_class = next_class; p->prio = newprio; + __setscheduler_dl_pi(newprio, policy, p, scope); } __setscheduler_uclamp(p, attr); check_class_changing(rq, p, prev_class); From e8ec80430bfa520e7352155d6ac632e527cba7aa Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Fri, 27 Mar 2026 23:30:00 -0700 Subject: [PATCH 008/697] bridge: mrp: reject zero test interval to avoid OOM panic [ Upstream commit fa6e24963342de4370e3a3c9af41e38277b74cf3 ] br_mrp_start_test() and br_mrp_start_in_test() accept the user-supplied interval value from netlink without validation. When interval is 0, usecs_to_jiffies(0) yields 0, causing the delayed work (br_mrp_test_work_expired / br_mrp_in_test_work_expired) to reschedule itself with zero delay. This creates a tight loop on system_percpu_wq that allocates and transmits MRP test frames at maximum rate, exhausting all system memory and causing a kernel panic via OOM deadlock. The same zero-interval issue applies to br_mrp_start_in_test_parse() for interconnect test frames. Use NLA_POLICY_MIN(NLA_U32, 1) in the nla_policy tables for both IFLA_BRIDGE_MRP_START_TEST_INTERVAL and IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL, so zero is rejected at the netlink attribute parsing layer before the value ever reaches the workqueue scheduling code. This is consistent with how other bridge subsystems (br_fdb, br_mst) enforce range constraints on netlink attributes. Fixes: 20f6a05ef635 ("bridge: mrp: Rework the MRP netlink interface") Fixes: 7ab1748e4ce6 ("bridge: mrp: Extend MRP netlink interface for configuring MRP interconnect") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Acked-by: Nikolay Aleksandrov Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260328063000.1845376-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/bridge/br_mrp_netlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bridge/br_mrp_netlink.c b/net/bridge/br_mrp_netlink.c index ce6f63c77cc0..86f0e75d6e34 100644 --- a/net/bridge/br_mrp_netlink.c +++ b/net/bridge/br_mrp_netlink.c @@ -196,7 +196,7 @@ static const struct nla_policy br_mrp_start_test_policy[IFLA_BRIDGE_MRP_START_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_TEST_RING_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_PERIOD] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_TEST_MONITOR] = { .type = NLA_U32 }, @@ -316,7 +316,7 @@ static const struct nla_policy br_mrp_start_in_test_policy[IFLA_BRIDGE_MRP_START_IN_TEST_MAX + 1] = { [IFLA_BRIDGE_MRP_START_IN_TEST_UNSPEC] = { .type = NLA_REJECT }, [IFLA_BRIDGE_MRP_START_IN_TEST_IN_ID] = { .type = NLA_U32 }, - [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = { .type = NLA_U32 }, + [IFLA_BRIDGE_MRP_START_IN_TEST_INTERVAL] = NLA_POLICY_MIN(NLA_U32, 1), [IFLA_BRIDGE_MRP_START_IN_TEST_MAX_MISS] = { .type = NLA_U32 }, [IFLA_BRIDGE_MRP_START_IN_TEST_PERIOD] = { .type = NLA_U32 }, }; From aae4a47073b12c23eb1d2c5401bda442fbe27bd1 Mon Sep 17 00:00:00 2001 From: Vladimir Yakovlev Date: Tue, 3 Mar 2026 01:20:17 +0300 Subject: [PATCH 009/697] spi: spi-dw-dma: fix print error log when wait finish transaction [ Upstream commit 3b46d61890632c8f8b117147b6923bff4b42ccb7 ] If an error occurs, the device may not have a current message. In this case, the system will crash. In this case, it's better to use dev from the struct ctlr (struct spi_controller*). Signed-off-by: Vladimir Yakovlev Link: https://patch.msgid.link/20260302222017.992228-2-vovchkir@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-dw-dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index b5bed02b7e50..31063f927092 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -271,7 +271,7 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) msecs_to_jiffies(ms)); if (ms == 0) { - dev_err(&dws->host->cur_msg->spi->dev, + dev_err(&dws->host->dev, "DMA transaction timed out\n"); return -ETIMEDOUT; } From 5da69a65b282d2276de22e5194ba0f88c836170c Mon Sep 17 00:00:00 2001 From: DaeMyung Kang Date: Tue, 28 Apr 2026 23:08:56 +0900 Subject: [PATCH 010/697] ksmbd: close durable scavenger races against m_fp_list lookups [ Upstream commit bf736184d063da1a552ffeff0481813599a182cc ] ksmbd_durable_scavenger() has two related races against any walker that iterates f_ci->m_fp_list, including ksmbd_lookup_fd_inode() (used by ksmbd_vfs_rename) and the share-mode checks in fs/smb/server/smb_common.c. (1) fp->node list-head reuse. Durable-preserved handles can remain linked on f_ci->m_fp_list after session teardown so share-mode checks still see them while the handle is reconnectable. The scavenger collected expired handles by adding fp->node to a local scavenger_list after removing them from the global durable idr. Because fp->node is the same list_head used by m_fp_list, list_add(&fp->node, &scavenger_list) overwrites the m_fp_list links and corrupts both lists. CONFIG_DEBUG_LIST can report this on the share-mode walk path. (2) Refcount race against m_fp_list walkers. The scavenger qualifies an expired durable handle with atomic_read(&fp->refcount) > 1 and fp->conn under global_ft.lock, removes fp from global_ft, then drops global_ft.lock before unlinking fp from m_fp_list and freeing it. During that gap fp is still linked on m_fp_list with f_state == FP_INITED. ksmbd_lookup_fd_inode() under m_lock read calls ksmbd_fp_get() (atomic_inc_not_zero on refcount that is still 1) and takes a live reference; the scavenger then unlinks and frees fp while the holder owns a reference, leading to UAF on the holder's subsequent ksmbd_fd_put() and on any field reads performed by a concurrent share-mode walker that iterates m_fp_list without taking ksmbd_fp_get() (smb_check_perm_dleases-like paths). Fix both: * Stop reusing fp->node as a scavenger-private list node. Remove one expired handle from global_ft under global_ft.lock, take an explicit transient reference, drop the lock, unlink fp->node from m_fp_list under f_ci->m_lock, then drop both the durable lifetime and transient references with atomic_sub_and_test(2, &fp->refcount). If the scavenger is the last putter the close runs there; otherwise an in-flight holder that already raced through the m_fp_list lookup owns the final close via its ksmbd_fd_put() path. The one-at-a-time disposal can rescan the durable idr when multiple handles expire in the same pass, but durable scavenging is a background expiration path and the final full scan recomputes min_timeout before the next wait. * Clear fp->persistent_id inside __ksmbd_remove_durable_fd() right after idr_remove(), so a delayed final close from a holder that snatched fp does not re-issue idr_remove() on a persistent id that idr_alloc_cyclic() in ksmbd_open_durable_fd() may have already handed out to a brand-new durable handle. * Bypass the per-conn open_files_count decrement in __put_fd_final() when fp is detached from any session table (fp->conn cleared by session_fd_check() at durable preserve -- paired with the volatile_id clear at unpublish, so checking fp->conn alone is sufficient). The walker that owns the final close runs from an unrelated work->conn whose stats.open_files_count never tracked this durable fp; without this guard the holder would underflow that unrelated counter. The two races are folded into one patch because patch (1) alone cleans up the corrupted list but leaves a deterministic UAF window for m_fp_list walkers that the transient-reference and persistent_id discipline in (2) close; bisecting onto an intermediate state would land on a UAF that pre-patch chaos merely made less reproducible. Validation: * CONFIG_DEBUG_LIST coverage for the list_head reuse path. * KASAN-enabled direct SMB2 durable-handle coverage that exercised ksmbd_durable_scavenger() and non-NULL ksmbd_lookup_fd_inode() returns while durable handles expired under concurrent rename lookups, with no KASAN, UAF, list-corruption, ODEBUG, or WARNING reports. * checkpatch --strict * make -j$(nproc) M=fs/smb/server Fixes: d484d621d40f ("ksmbd: add durable scavenger timer") Signed-off-by: DaeMyung Kang Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/vfs_cache.c | 104 ++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 27 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index d29cc1d01bd2..a8fed467e9b6 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -325,6 +325,14 @@ static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp) return; idr_remove(global_ft.idr, fp->persistent_id); + /* + * Clear persistent_id so a later __ksmbd_close_fd() that runs from a + * delayed putter (e.g. when a concurrent ksmbd_lookup_fd_inode() + * walker held the final reference) does not re-issue idr_remove() on + * an id that idr_alloc_cyclic() may have already handed out to a new + * durable handle. + */ + fp->persistent_id = KSMBD_NO_FID; } static void ksmbd_remove_durable_fd(struct ksmbd_file *fp) @@ -417,6 +425,20 @@ static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft, static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp) { + /* + * Detached durable fp -- session_fd_check() cleared fp->conn at + * preserve, so this fp is no longer tracked by any conn's + * stats.open_files_count. This happens when + * ksmbd_scavenger_dispose_dh() hands the final close off to an + * m_fp_list walker (e.g. ksmbd_lookup_fd_inode()) whose work->conn + * is unrelated to the conn that originally opened the handle; close + * via the NULL-ft path so we do not underflow that unrelated + * counter. + */ + if (!fp->conn) { + __ksmbd_close_fd(NULL, fp); + return; + } __ksmbd_close_fd(&work->sess->file_table, fp); atomic_dec(&work->conn->stats.open_files_count); } @@ -788,24 +810,37 @@ static bool ksmbd_durable_scavenger_alive(void) return true; } -static void ksmbd_scavenger_dispose_dh(struct list_head *head) +static void ksmbd_scavenger_dispose_dh(struct ksmbd_file *fp) { - while (!list_empty(head)) { - struct ksmbd_file *fp; + /* + * Durable-preserved fp can remain linked on f_ci->m_fp_list for + * share-mode checks. Unlink it before final close; fp->node is not + * available as a scavenger-private list node because re-adding it to + * another list corrupts m_fp_list. + */ + down_write(&fp->f_ci->m_lock); + list_del_init(&fp->node); + up_write(&fp->f_ci->m_lock); - fp = list_first_entry(head, struct ksmbd_file, node); - list_del_init(&fp->node); + /* + * Drop both the durable lifetime reference and the transient reference + * taken by the scavenger under global_ft.lock. If a concurrent + * ksmbd_lookup_fd_inode() (or any other m_fp_list walker) snatched fp + * before the unlink above, that holder owns the final close via + * ksmbd_fd_put() -> __ksmbd_close_fd(). Otherwise the scavenger is + * the last putter and finalises fp here. + */ + if (atomic_sub_and_test(2, &fp->refcount)) __ksmbd_close_fd(NULL, fp); - } } static int ksmbd_durable_scavenger(void *dummy) { struct ksmbd_file *fp = NULL; + struct ksmbd_file *expired_fp; unsigned int id; unsigned int min_timeout = 1; bool found_fp_timeout; - LIST_HEAD(scavenger_list); unsigned long remaining_jiffies; __module_get(THIS_MODULE); @@ -815,8 +850,6 @@ static int ksmbd_durable_scavenger(void *dummy) if (try_to_freeze()) continue; - found_fp_timeout = false; - remaining_jiffies = wait_event_timeout(dh_wq, ksmbd_durable_scavenger_alive() == false, __msecs_to_jiffies(min_timeout)); @@ -825,23 +858,39 @@ static int ksmbd_durable_scavenger(void *dummy) else min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; - write_lock(&global_ft.lock); - idr_for_each_entry(global_ft.idr, fp, id) { - if (!fp->durable_timeout) - continue; - - if (atomic_read(&fp->refcount) > 1 || - fp->conn) - continue; - - found_fp_timeout = true; - if (fp->durable_scavenger_timeout <= - jiffies_to_msecs(jiffies)) { - __ksmbd_remove_durable_fd(fp); - list_add(&fp->node, &scavenger_list); - } else { + do { + expired_fp = NULL; + found_fp_timeout = false; + + write_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { unsigned long durable_timeout; + if (!fp->durable_timeout) + continue; + + if (atomic_read(&fp->refcount) > 1 || + fp->conn) + continue; + + found_fp_timeout = true; + if (fp->durable_scavenger_timeout <= + jiffies_to_msecs(jiffies)) { + __ksmbd_remove_durable_fd(fp); + /* + * Take a transient reference so fp + * cannot be freed by an in-flight + * ksmbd_lookup_fd_inode() that found + * it through f_ci->m_fp_list while we + * drop global_ft.lock and reach the + * m_fp_list unlink in + * ksmbd_scavenger_dispose_dh(). + */ + atomic_inc(&fp->refcount); + expired_fp = fp; + break; + } + durable_timeout = fp->durable_scavenger_timeout - jiffies_to_msecs(jiffies); @@ -849,10 +898,11 @@ static int ksmbd_durable_scavenger(void *dummy) if (min_timeout > durable_timeout) min_timeout = durable_timeout; } - } - write_unlock(&global_ft.lock); + write_unlock(&global_ft.lock); - ksmbd_scavenger_dispose_dh(&scavenger_list); + if (expired_fp) + ksmbd_scavenger_dispose_dh(expired_fp); + } while (expired_fp); if (found_fp_timeout == false) break; From 91f89c1d83e80417629791fcef6af8140d7d01c8 Mon Sep 17 00:00:00 2001 From: Asim Viladi Oglu Manizada Date: Sat, 16 May 2026 21:15:39 +0000 Subject: [PATCH 011/697] smb: client: reject userspace cifs.spnego descriptions commit 3da1fdf4efbc490041eb4f836bf596201203f8f2 upstream. cifs.spnego key descriptions contain authority-bearing fields such as pid, uid, creduid, and upcall_target that cifs.upcall treats as kernel-originating inputs. However, userspace can also create keys of this type through request_key(2) or add_key(2), allowing those fields to be supplied without CIFS origin. Only accept cifs.spnego descriptions while CIFS is using its private spnego_cred to request the key. Fixes: f1d662a7d5e5 ("[CIFS] Add upcall files for cifs to use spnego/kerberos") Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Reviewed-by: David Howells Signed-off-by: Asim Viladi Oglu Manizada Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifs_spnego.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c index 9891f55bac1e..60b4147d0eea 100644 --- a/fs/smb/client/cifs_spnego.c +++ b/fs/smb/client/cifs_spnego.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -40,12 +41,27 @@ cifs_spnego_key_destroy(struct key *key) kfree(key->payload.data[0]); } +static int +cifs_spnego_key_vet_description(const char *description) +{ + /* + * cifs.spnego descriptions are authority-bearing inputs to cifs.upcall. + * They are only valid when produced by CIFS while using the private + * spnego_cred installed below. Do not let userspace create this type + * of key through request_key(2)/add_key(2), since the helper treats + * pid/uid/creduid/upcall_target as kernel-originating fields. + */ + if (current_cred() != spnego_cred) + return -EPERM; + return 0; +} /* * keytype for CIFS spnego keys */ struct key_type cifs_spnego_key_type = { .name = "cifs.spnego", + .vet_description = cifs_spnego_key_vet_description, .instantiate = cifs_spnego_key_instantiate, .destroy = cifs_spnego_key_destroy, .describe = user_describe, From 375d5a17dc8d7668c2479dc4cd329838c58d8e4f Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 31 Oct 2025 20:33:07 +0200 Subject: [PATCH 012/697] dt-bindings: soc: bcm: Add bcm2712 compatible commit 34194cb385033656d347ebe45c241e4739a58125 upstream. Add bcm2712-pm compatible and update the bindings to satisfy it's requirements. The PM hardware block inside bcm2712 lacks the "asb" and "rpivid_asb" register ranges and also does not have clocks, update the bindings accordingly. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Acked-by: Conor Dooley Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- .../bindings/soc/bcm/brcm,bcm2835-pm.yaml | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml index e28ef198a801..039c8e4a4c51 100644 --- a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml +++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml @@ -13,23 +13,21 @@ description: | maintainers: - Nicolas Saenz Julienne -allOf: - - $ref: /schemas/watchdog/watchdog.yaml# - properties: compatible: items: - enum: - brcm,bcm2835-pm - brcm,bcm2711-pm + - brcm,bcm2712-pm - const: brcm,bcm2835-pm-wdt reg: - minItems: 2 + minItems: 1 maxItems: 3 reg-names: - minItems: 2 + minItems: 1 items: - const: pm - const: asb @@ -62,7 +60,35 @@ required: - reg - "#power-domain-cells" - "#reset-cells" - - clocks + +allOf: + - $ref: /schemas/watchdog/watchdog.yaml# + + - if: + properties: + compatible: + contains: + enum: + - brcm,bcm2835-pm + - brcm,bcm2711-pm + then: + required: + - clocks + + properties: + reg: + minItems: 2 + + reg-names: + minItems: 2 + + else: + properties: + reg: + maxItems: 1 + + reg-names: + maxItems: 1 additionalProperties: false From ed915823d4692aa4fc9d8ece4746ea1a7269cfde Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Fri, 31 Oct 2025 20:33:09 +0200 Subject: [PATCH 013/697] arm64: dts: broadcom: bcm2712: Add watchdog DT node commit 37c3a91e9730e274fe2eca9703033ae0f154cb62 upstream. Add watchdog device-tree node for bcm2712 SoC. Signed-off-by: Stanimir Varbanov Link: https://lore.kernel.org/r/20251031183309.1163384-5-svarbanov@suse.de Signed-off-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi index 205b87f557d6..d771694787b2 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi @@ -250,6 +250,15 @@ status = "disabled"; }; + pm: watchdog@7d200000 { + compatible = "brcm,bcm2712-pm", "brcm,bcm2835-pm-wdt"; + reg = <0x7d200000 0x604>; + reg-names = "pm"; + #power-domain-cells = <1>; + #reset-cells = <1>; + system-power-controller; + }; + pinctrl: pinctrl@7d504100 { compatible = "brcm,bcm2712c0-pinctrl"; reg = <0x7d504100 0x30>; From 9d11e4b1db1ce4057251d90ea1a670f2339ab664 Mon Sep 17 00:00:00 2001 From: Stanimir Varbanov Date: Wed, 17 Sep 2025 09:32:32 +0300 Subject: [PATCH 014/697] mfd: bcm2835-pm: Add support for BCM2712 commit 30ed024fb0768e9353f21d1d9e6960b7028acdfa upstream. The BCM2712 SoC has PM block but lacks the "asb" and "rpivid_asb" register spaces, and doesn't need clock(s). Add a compatible string for bcm2712 to allow probe of bcm2835-wdt and bcm2835-power drivers. Signed-off-by: Stanimir Varbanov Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20250917063233.1270-4-svarbanov@suse.de Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/bcm2835-pm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c index 3cb2b9423121..8bed59816e82 100644 --- a/drivers/mfd/bcm2835-pm.c +++ b/drivers/mfd/bcm2835-pm.c @@ -108,6 +108,7 @@ static const struct of_device_id bcm2835_pm_of_match[] = { { .compatible = "brcm,bcm2835-pm-wdt", }, { .compatible = "brcm,bcm2835-pm", }, { .compatible = "brcm,bcm2711-pm", }, + { .compatible = "brcm,bcm2712-pm", }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match); From 62ee00c1042ca9e0c4c1f9587d49197a6c1c6bac Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 May 2026 09:38:59 +0200 Subject: [PATCH 015/697] ata: libata-scsi: improve readability of ata_scsi_qc_issue() commit 360190bd965f93794d5f5685a6de22ce6da2b672 upstream. Improve readability of ata_scsi_qc_issue(). No functional changes. Tested-by: Tommy Kelly Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-scsi.c | 43 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e7c78b8d3c2c..73903f5997ca 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1767,7 +1767,7 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) int ret; if (!ap->ops->qc_defer) - goto issue; + goto issue_qc; /* * If we already have a deferred qc, then rely on the SCSI layer to @@ -1786,38 +1786,37 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) break; case ATA_DEFER_LINK: ret = SCSI_MLQUEUE_DEVICE_BUSY; - break; + goto defer_qc; case ATA_DEFER_PORT: ret = SCSI_MLQUEUE_HOST_BUSY; - break; + goto defer_qc; default: WARN_ON_ONCE(1); ret = SCSI_MLQUEUE_HOST_BUSY; - break; + goto defer_qc; } - if (ret) { - /* - * We must defer this qc: if this is not an NCQ command, keep - * this qc as a deferred one and report to the SCSI layer that - * we issued it so that it is not requeued. The deferred qc will - * be issued with the port deferred_qc_work once all on-going - * commands complete. - */ - if (!ata_is_ncq(qc->tf.protocol)) { - ap->deferred_qc = qc; - return 0; - } +issue_qc: + ata_qc_issue(qc); + return 0; - /* Force a requeue of the command to defer its execution. */ - ata_qc_free(qc); - return ret; +defer_qc: + /* + * We must defer this qc: if this is not an NCQ command, keep + * this qc as a deferred one and report to the SCSI layer that + * we issued it so that it is not requeued. The deferred qc will + * be issued with the port deferred_qc_work once all on-going + * commands complete. + */ + if (!ata_is_ncq(qc->tf.protocol)) { + ap->deferred_qc = qc; + return 0; } -issue: - ata_qc_issue(qc); + /* Force a requeue of the command to defer its execution. */ + ata_qc_free(qc); - return 0; + return ret; } /** From f207ebd5656e7eadb6b17e23ed763f4c3ac8637c Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 May 2026 09:39:00 +0200 Subject: [PATCH 016/697] ata: libata-scsi: do not use the deferred QC feature for ATA_DEFER_PORT commit ce4548807d2e4ae48fd0dbe38865467369877913 upstream. The deferred QC feature was meant to handle mixed NCQ and non-NCQ commands, i.e. for return value ATA_DEFER_LINK. ATA_DEFER_PORT is returned by PATA drivers, but also certain SATA drivers like sata_mv and sata_sil24 that uses ap->excl_link to workaround hardware bugs in these HBAs. Regardless of the reason, using the deferred QC feature for ATA_DEFER_PORT is always wrong, and will break the ap->excl_link usage of the SATA drivers that rely on that feature. Modify ata_scsi_qc_issue() to only use the deferred QC feature when mixing NCQ and non-NCQ commands, i.e. ATA_DEFER_LINK. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Tested-by: Tommy Kelly Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-scsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 73903f5997ca..d837389f40f7 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1789,11 +1789,11 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) goto defer_qc; case ATA_DEFER_PORT: ret = SCSI_MLQUEUE_HOST_BUSY; - goto defer_qc; + goto free_qc; default: WARN_ON_ONCE(1); ret = SCSI_MLQUEUE_HOST_BUSY; - goto defer_qc; + goto free_qc; } issue_qc: @@ -1813,6 +1813,7 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) return 0; } +free_qc: /* Force a requeue of the command to defer its execution. */ ata_qc_free(qc); From 4e6eada8de381aaa7c8d8abc724327a48e2205de Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 May 2026 09:39:01 +0200 Subject: [PATCH 017/697] ata: libata-scsi: do not use the deferred QC feature on PMPs with CBS commit f233124fb36cd57ef09f96d517a38ab4b902e15e upstream. When using Port Multipliers (PMPs) with Command-Based Switching (CBS), you can only issue commands to one link at a time. For PMPs with CBS, there is already code to handle commands being sent to different links in sata_pmp_qc_defer_cmd_switch() using ap->excl_link. sata_sil24 also makes use of ap->excl_link. A user on the list reported that commit 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") broke PMPs with CBS. The commit introduced code that stores a deferred qc in ap->deferred_qc, to later be issued via a workqueue. It turns out that this change is incompatible with the existing ap->excl_link handling used by PMPs with CBS. Thus, modify sata_pmp_qc_defer_cmd_switch() and sil24_qc_defer() to return ATA_DEFER_LINK_EXCL, and make sure that the deferred QC handling via workqueue is not used for this return value. This way, PMPs with CBS will work once again. Note that the starvation referenced in commit 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") can only happen on libsas ports, and libsas does not support Port Multipliers, thus there is no harm of reverting back to the previous way of deferring commands for PMPs with CBS. Non-libsas ports connected to anything but a PMP with CBS (e.g. a normal drive or a PMP with FBS) will continue using the deferred workqueue, since it does result in lower completion latencies for non-NCQ commands, even though the workqueue is not strictly needed to avoid starvation for non-libsas ports. If we want to modify the scope of the workqueue issuing to also handle PMPs with CBS, then we should ensure that we can save both NCQ and non-NCQ commands in ap->deferred_qc, while also removing the existing PMP CBS handling using ap->excl_link, such that we don't duplicate features. While at it, also add a comment explaining how the ap->excl_link mechanism works. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Tested-by: Tommy Kelly Reported-by: Tommy Kelly Closes: https://lore.kernel.org/linux-ide/ce09cc21-a8e9-4845-b205-35411e22fba9@tkel.ly/ Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-pmp.c | 13 ++++++++++++- drivers/ata/libata-scsi.c | 8 ++++++++ drivers/ata/sata_sil24.c | 6 +++++- include/linux/libata.h | 1 + 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index 57023324a56f..b4adf0eb2ed5 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -110,13 +110,24 @@ int sata_pmp_qc_defer_cmd_switch(struct ata_queued_cmd *qc) { struct ata_link *link = qc->dev->link; struct ata_port *ap = link->ap; + int ret; if (ap->excl_link == NULL || ap->excl_link == link) { if (ap->nr_active_links == 0 || ata_link_active(link)) { qc->flags |= ATA_QCFLAG_CLEAR_EXCL; - return ata_std_qc_defer(qc); + ret = ata_std_qc_defer(qc); + if (ret == ATA_DEFER_LINK) + return ATA_DEFER_LINK_EXCL; + return ret; } + /* + * Note: ap->excl_link contains the link that is next in line, + * i.e. implicit round robin. If there is only one link + * dispatching, ap->excl_link will be left unclaimed, allowing + * other links to set ap->excl_link, ensuring that the currently + * active link cannot queue any more. + */ ap->excl_link = link; } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index d837389f40f7..bbedbee6b85e 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1787,6 +1787,14 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) case ATA_DEFER_LINK: ret = SCSI_MLQUEUE_DEVICE_BUSY; goto defer_qc; + case ATA_DEFER_LINK_EXCL: + /* + * Drivers making use of ap->excl_link cannot store the QC in + * ap->deferred_qc, because the ap->excl_link handling is + * incompatible with the ap->deferred_qc workqueue handling. + */ + ret = SCSI_MLQUEUE_DEVICE_BUSY; + goto free_qc; case ATA_DEFER_PORT: ret = SCSI_MLQUEUE_HOST_BUSY; goto free_qc; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index d642ece9f07a..57f1081b86db 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -789,6 +789,7 @@ static int sil24_qc_defer(struct ata_queued_cmd *qc) struct ata_link *link = qc->dev->link; struct ata_port *ap = link->ap; u8 prot = qc->tf.protocol; + int ret; /* * There is a bug in the chip: @@ -826,7 +827,10 @@ static int sil24_qc_defer(struct ata_queued_cmd *qc) qc->flags |= ATA_QCFLAG_CLEAR_EXCL; } - return ata_std_qc_defer(qc); + ret = ata_std_qc_defer(qc); + if (ret == ATA_DEFER_LINK) + return ATA_DEFER_LINK_EXCL; + return ret; } static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc) diff --git a/include/linux/libata.h b/include/linux/libata.h index 3b8bdea8516d..03cd68a4da66 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -335,6 +335,7 @@ enum { /* return values for ->qc_defer */ ATA_DEFER_LINK = 1, ATA_DEFER_PORT = 2, + ATA_DEFER_LINK_EXCL = 3, /* desc_len for ata_eh_info and context */ ATA_EH_DESC_LEN = 80, From 515de0a3b6c12577dcd7ed7c7b2875b18a316935 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 May 2026 09:39:02 +0200 Subject: [PATCH 018/697] ata: libata-scsi: do not needlessly defer commands when using PMP with FBS commit 759e8756da00aa115d504a18155b1d1ee1cc12e8 upstream. The ACS specification does not allow a non-NCQ command to be issued while an NCQ command is outstanding. Commit 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") introduced a feature where a deferred non-NCQ command gets issued from a workqueue. The design stores a single non-NCQ command per port. However, when using Port Multipliers (PMPs), specifically PMPs that support FIS-Based Switching (FBS), non-NCQ and NCQ commands can be mixed on the same port, just not for the same link, see e.g. ata_std_qc_defer() which is, and always has operated on a per-link basis. Therefore, move the deferred_qc from struct ata_port to struct ata_link. This way, when using a PMP with FBS, we will not needlessly defer commands to all other links, just because one link issued a non-NCQ command while having an NCQ command outstanding. Only commands for that specific link will be deferred. This is in line with how PMPs with FBS worked before commit 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation"). Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Tested-by: Tommy Kelly Reviewed-by: Damien Le Moal Signed-off-by: Niklas Cassel Signed-off-by: Greg Kroah-Hartman --- drivers/ata/libata-core.c | 9 ++++--- drivers/ata/libata-eh.c | 8 +++--- drivers/ata/libata-pmp.c | 5 +++- drivers/ata/libata-scsi.c | 54 ++++++++++++++++++++++----------------- include/linux/libata.h | 6 ++--- 5 files changed, 47 insertions(+), 35 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 4fb8990d22d5..5ee4adc62342 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -5499,6 +5499,7 @@ void ata_link_init(struct ata_port *ap, struct ata_link *link, int pmp) link->pmp = pmp; link->active_tag = ATA_TAG_POISON; link->hw_sata_spd_limit = UINT_MAX; + INIT_WORK(&link->deferred_qc_work, ata_scsi_deferred_qc_work); /* can't use iterator, ap isn't initialized yet */ for (i = 0; i < ATA_MAX_DEVICES; i++) { @@ -5581,7 +5582,6 @@ struct ata_port *ata_port_alloc(struct ata_host *host) mutex_init(&ap->scsi_scan_mutex); INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug); INIT_DELAYED_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan); - INIT_WORK(&ap->deferred_qc_work, ata_scsi_deferred_qc_work); INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); init_completion(&ap->park_req_pending); @@ -6204,12 +6204,15 @@ static void ata_port_detach(struct ata_port *ap) /* It better be dead now and not have any remaining deferred qc. */ WARN_ON(!(ap->pflags & ATA_PFLAG_UNLOADED)); - WARN_ON(ap->deferred_qc); - cancel_work_sync(&ap->deferred_qc_work); cancel_delayed_work_sync(&ap->hotplug_task); cancel_delayed_work_sync(&ap->scsi_rescan_task); + ata_for_each_link(link, ap, PMP_FIRST) { + WARN_ON(link->deferred_qc); + cancel_work_sync(&link->deferred_qc_work); + } + /* Delete port multiplier link transport devices */ if (ap->pmp_link) { int i; diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 23be85418b3b..5e8a63206108 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -643,11 +643,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, if (qc->scsicmd != scmd) continue; if ((qc->flags & ATA_QCFLAG_ACTIVE) || - qc == ap->deferred_qc) + qc == qc->dev->link->deferred_qc) break; } - if (i < ATA_MAX_QUEUE && qc == ap->deferred_qc) { + if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) { /* * This is a deferred command that timed out while * waiting for the command queue to drain. Since the qc @@ -658,8 +658,8 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, * deferred qc work from issuing this qc. */ WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); - ap->deferred_qc = NULL; - cancel_work(&ap->deferred_qc_work); + qc->dev->link->deferred_qc = NULL; + cancel_work(&qc->dev->link->deferred_qc_work); set_host_byte(scmd, DID_TIME_OUT); scsi_eh_finish_cmd(scmd, &ap->eh_done_q); } else if (i < ATA_MAX_QUEUE) { diff --git a/drivers/ata/libata-pmp.c b/drivers/ata/libata-pmp.c index b4adf0eb2ed5..48ac09d9031e 100644 --- a/drivers/ata/libata-pmp.c +++ b/drivers/ata/libata-pmp.c @@ -582,8 +582,11 @@ static void sata_pmp_detach(struct ata_device *dev) if (ap->ops->pmp_detach) ap->ops->pmp_detach(ap); - ata_for_each_link(tlink, ap, EDGE) + ata_for_each_link(tlink, ap, EDGE) { + WARN_ON(tlink->deferred_qc); + cancel_work_sync(&tlink->deferred_qc_work); ata_eh_detach_dev(tlink->device); + } spin_lock_irqsave(ap->lock, flags); ap->nr_pmp_links = 0; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index bbedbee6b85e..c7d2addf3487 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1659,8 +1659,9 @@ static void ata_qc_done(struct ata_queued_cmd *qc) void ata_scsi_deferred_qc_work(struct work_struct *work) { - struct ata_port *ap = - container_of(work, struct ata_port, deferred_qc_work); + struct ata_link *link = + container_of(work, struct ata_link, deferred_qc_work); + struct ata_port *ap = link->ap; struct ata_queued_cmd *qc; unsigned long flags; @@ -1671,10 +1672,10 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) * such case, we should not need any more deferring the qc, so warn if * qc_defer() says otherwise. */ - qc = ap->deferred_qc; + qc = link->deferred_qc; if (qc && !ata_port_eh_scheduled(ap)) { WARN_ON_ONCE(ap->ops->qc_defer(qc)); - ap->deferred_qc = NULL; + link->deferred_qc = NULL; ata_qc_issue(qc); } @@ -1683,8 +1684,7 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) void ata_scsi_requeue_deferred_qc(struct ata_port *ap) { - struct ata_queued_cmd *qc = ap->deferred_qc; - struct scsi_cmnd *scmd; + struct ata_link *link; lockdep_assert_held(ap->lock); @@ -1693,20 +1693,25 @@ void ata_scsi_requeue_deferred_qc(struct ata_port *ap) * do not try to be smart about what to do with this deferred command * and simply requeue it by completing it with DID_REQUEUE. */ - if (!qc) - return; - - scmd = qc->scsicmd; - ap->deferred_qc = NULL; - cancel_work(&ap->deferred_qc_work); - ata_qc_free(qc); - scmd->result = (DID_REQUEUE << 16); - scsi_done(scmd); + ata_for_each_link(link, ap, PMP_FIRST) { + struct ata_queued_cmd *qc = link->deferred_qc; + struct scsi_cmnd *scmd; + + if (qc) { + scmd = qc->scsicmd; + link->deferred_qc = NULL; + cancel_work(&link->deferred_qc_work); + ata_qc_free(qc); + scmd->result = (DID_REQUEUE << 16); + scsi_done(scmd); + } + } } -static void ata_scsi_schedule_deferred_qc(struct ata_port *ap) +static void ata_scsi_schedule_deferred_qc(struct ata_link *link) { - struct ata_queued_cmd *qc = ap->deferred_qc; + struct ata_queued_cmd *qc = link->deferred_qc; + struct ata_port *ap = link->ap; lockdep_assert_held(ap->lock); @@ -1723,12 +1728,12 @@ static void ata_scsi_schedule_deferred_qc(struct ata_port *ap) return; } if (!ap->ops->qc_defer(qc)) - queue_work(system_highpri_wq, &ap->deferred_qc_work); + queue_work(system_highpri_wq, &link->deferred_qc_work); } static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; + struct ata_link *link = qc->dev->link; struct scsi_cmnd *cmd = qc->scsicmd; u8 *cdb = cmd->cmnd; bool have_sense = qc->flags & ATA_QCFLAG_SENSE_VALID; @@ -1759,11 +1764,12 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) ata_qc_done(qc); - ata_scsi_schedule_deferred_qc(ap); + ata_scsi_schedule_deferred_qc(link); } static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) { + struct ata_link *link = qc->dev->link; int ret; if (!ap->ops->qc_defer) @@ -1774,7 +1780,7 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) * requeue and defer all incoming commands until the deferred qc is * processed, once all on-going commands complete. */ - if (ap->deferred_qc) { + if (link->deferred_qc) { ata_qc_free(qc); return SCSI_MLQUEUE_DEVICE_BUSY; } @@ -1790,8 +1796,8 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) case ATA_DEFER_LINK_EXCL: /* * Drivers making use of ap->excl_link cannot store the QC in - * ap->deferred_qc, because the ap->excl_link handling is - * incompatible with the ap->deferred_qc workqueue handling. + * link->deferred_qc, because the ap->excl_link handling is + * incompatible with the link->deferred_qc workqueue handling. */ ret = SCSI_MLQUEUE_DEVICE_BUSY; goto free_qc; @@ -1817,7 +1823,7 @@ static int ata_scsi_qc_issue(struct ata_port *ap, struct ata_queued_cmd *qc) * commands complete. */ if (!ata_is_ncq(qc->tf.protocol)) { - ap->deferred_qc = qc; + link->deferred_qc = qc; return 0; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 03cd68a4da66..5c3f98a418c3 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -855,6 +855,9 @@ struct ata_link { unsigned int sata_spd; /* current SATA PHY speed */ enum ata_lpm_policy lpm_policy; + struct work_struct deferred_qc_work; + struct ata_queued_cmd *deferred_qc; + /* record runtime error info, protected by host_set lock */ struct ata_eh_info eh_info; /* EH context */ @@ -900,9 +903,6 @@ struct ata_port { u64 qc_active; int nr_active_links; /* #links with active qcs */ - struct work_struct deferred_qc_work; - struct ata_queued_cmd *deferred_qc; - struct ata_link link; /* host default link */ struct ata_link *slave_link; /* see ata_slave_link_init() */ From 523cd0ea0324202704f24bd27eacd742916c8b8b Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 27 May 2026 12:43:10 -0400 Subject: [PATCH 019/697] Revert "ice: fix double-free of tx_buf skb" This reverts commit 7cb19ec8ac087f33bee60f5d6054b284a5b9bb6f. Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_txrx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index e0774a640955..90dbe5266ce7 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -2594,9 +2594,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) ice_trace(xmit_frame_ring, tx_ring, skb); - /* record the location of the first descriptor for this packet */ - first = &tx_ring->tx_buf[tx_ring->next_to_use]; - count = ice_xmit_desc_count(skb); if (ice_chk_linearize(skb, count)) { if (__skb_linearize(skb)) @@ -2622,6 +2619,8 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) offload.tx_ring = tx_ring; + /* record the location of the first descriptor for this packet */ + first = &tx_ring->tx_buf[tx_ring->next_to_use]; first->skb = skb; first->type = ICE_TX_BUF_SKB; first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN); @@ -2686,7 +2685,6 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) out_drop: ice_trace(xmit_frame_ring_drop, tx_ring, skb); dev_kfree_skb_any(skb); - first->type = ICE_TX_BUF_EMPTY; return NETDEV_TX_OK; } From 18a08b87db713d50b4fb0ff3e971c36f6610c666 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 27 May 2026 12:43:10 -0400 Subject: [PATCH 020/697] Revert "ice: Remove jumbo_remove step from TX path" This reverts commit 55500245ec0420df96b2a89444aabf0cff2c60bc. Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_txrx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c index 90dbe5266ce7..73f08d02f9c7 100644 --- a/drivers/net/ethernet/intel/ice/ice_txrx.c +++ b/drivers/net/ethernet/intel/ice/ice_txrx.c @@ -2594,6 +2594,9 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_tx_ring *tx_ring) ice_trace(xmit_frame_ring, tx_ring, skb); + if (unlikely(ipv6_hopopt_jumbo_remove(skb))) + goto out_drop; + count = ice_xmit_desc_count(skb); if (ice_chk_linearize(skb, count)) { if (__skb_linearize(skb)) From fa4b91eea4331e7c24aa2d7855583d062a73e4ea Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 26 May 2026 21:31:19 +0800 Subject: [PATCH 021/697] drm/vblank: Add vblank timer [ Upstream commit 74afeb8128502a529041a2566febd26053a7be11 ] The vblank timer simulates a vblank interrupt for hardware without support. Rate-limits the display update frequency. DRM drivers for hardware without vblank support apply display updates ASAP. A vblank event informs DRM clients of the completed update. Userspace compositors immediately schedule the next update, which creates significant load on virtualization outputs. Display updates are usually fast on virtualization outputs, as their framebuffers are in regular system memory and there's no hardware vblank interrupt to throttle the update rate. The vblank timer is a HR timer that signals the vblank in software. It limits the update frequency of a DRM driver similar to a hardware vblank interrupt. The timer is not synchronized to the actual vblank interval of the display. The code has been adopted from vkms, which added the funtionality in commit 3a0709928b17 ("drm/vkms: Add vblank events simulated by hrtimers"). The new implementation is part of the existing vblank support, which sets up the timer automatically. Drivers only have to start and cancel the vblank timer as part of enabling and disabling the CRTC. The new vblank helper library provides callbacks for struct drm_crtc_funcs. The standard way for handling vblank is to call drm_crtc_handle_vblank(). Drivers that require additional processing, such as vkms, can init handle_vblank_timeout in struct drm_crtc_helper_funcs to refer to their timeout handler. There's a possible deadlock between drm_crtc_handle_vblank() and hrtimer_cancel(). [1] The implementation avoids to call hrtimer_cancel() directly and instead signals to the timer function to not restart itself. v4: - fix possible race condition between timeout and atomic commit (Michael) v3: - avoid deadlock when cancelling timer (Ville, Lyude) v2: - implement vblank timer entirely in vblank helpers - downgrade overrun warning to debug - fix docs Signed-off-by: Thomas Zimmermann Tested-by: Louis Chauvet Reviewed-by: Louis Chauvet Reviewed-by: Javier Martinez Canillas Tested-by: Michael Kelley Link: https://lore.kernel.org/all/20250510094757.4174662-1-zengheng4@huawei.com/ # [1] Link: https://lore.kernel.org/r/20250916083816.30275-2-tzimmermann@suse.de Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Sasha Levin --- Documentation/gpu/drm-kms-helpers.rst | 12 ++ drivers/gpu/drm/Makefile | 3 +- drivers/gpu/drm/drm_vblank.c | 172 ++++++++++++++++++++++- drivers/gpu/drm/drm_vblank_helper.c | 96 +++++++++++++ include/drm/drm_modeset_helper_vtables.h | 12 ++ include/drm/drm_vblank.h | 32 +++++ include/drm/drm_vblank_helper.h | 33 +++++ 7 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/drm_vblank_helper.c create mode 100644 include/drm/drm_vblank_helper.h diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 5139705089f2..781129f78b06 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -92,6 +92,18 @@ GEM Atomic Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_gem_atomic_helper.c :export: +VBLANK Helper Reference +----------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c + :doc: overview + +.. kernel-doc:: include/drm/drm_vblank_helper.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c + :export: + Simple KMS Helper Reference =========================== diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 742f0d590c5a..b248e64587ed 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -152,7 +152,8 @@ drm_kms_helper-y := \ drm_plane_helper.o \ drm_probe_helper.o \ drm_self_refresh_helper.o \ - drm_simple_kms_helper.o + drm_simple_kms_helper.o \ + drm_vblank_helper.o drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 46f59883183d..61e211fd3c9c 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -136,8 +136,17 @@ * vblanks after a timer has expired, which can be configured through the * ``vblankoffdelay`` module parameter. * - * Drivers for hardware without support for vertical-blanking interrupts - * must not call drm_vblank_init(). For such drivers, atomic helpers will + * Drivers for hardware without support for vertical-blanking interrupts can + * use DRM vblank timers to send vblank events at the rate of the current + * display mode's refresh. While not synchronized to the hardware's + * vertical-blanking regions, the timer helps DRM clients and compositors to + * adapt their update cycle to the display output. Drivers should set up + * vblanking as usual, but call drm_crtc_vblank_start_timer() and + * drm_crtc_vblank_cancel_timer() as part of their atomic mode setting. + * See also DRM vblank helpers for more information. + * + * Drivers without support for vertical-blanking interrupts nor timers must + * not call drm_vblank_init(). For these drivers, atomic helpers will * automatically generate fake vblank events as part of the display update. * This functionality also can be controlled by the driver by enabling and * disabling struct drm_crtc_state.no_vblank. @@ -508,6 +517,9 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && drm_core_check_feature(dev, DRIVER_MODESET)); + if (vblank->vblank_timer.crtc) + hrtimer_cancel(&vblank->vblank_timer.timer); + drm_vblank_destroy_worker(vblank); timer_delete_sync(&vblank->disable_timer); } @@ -2162,3 +2174,159 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, return ret; } +/* + * VBLANK timer + */ + +static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer) +{ + struct drm_vblank_crtc_timer *vtimer = + container_of(timer, struct drm_vblank_crtc_timer, timer); + struct drm_crtc *crtc = vtimer->crtc; + const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + struct drm_device *dev = crtc->dev; + unsigned long flags; + ktime_t interval; + u64 ret_overrun; + bool succ; + + spin_lock_irqsave(&vtimer->interval_lock, flags); + interval = vtimer->interval; + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + if (!interval) + return HRTIMER_NORESTART; + + ret_overrun = hrtimer_forward_now(&vtimer->timer, interval); + if (ret_overrun != 1) + drm_dbg_vbl(dev, "vblank timer overrun\n"); + + if (crtc_funcs->handle_vblank_timeout) + succ = crtc_funcs->handle_vblank_timeout(crtc); + else + succ = drm_crtc_handle_vblank(crtc); + if (!succ) + return HRTIMER_NORESTART; + + return HRTIMER_RESTART; +} + +/** + * drm_crtc_vblank_start_timer - Starts the vblank timer on the given CRTC + * @crtc: the CRTC + * + * Drivers should call this function from their CRTC's enable_vblank + * function to start a vblank timer. The timer will fire after the duration + * of a full frame. drm_crtc_vblank_cancel_timer() disables a running timer. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drm_crtc_vblank_start_timer(struct drm_crtc *crtc) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + unsigned long flags; + + if (!vtimer->crtc) { + /* + * Set up the data structures on the first invocation. + */ + vtimer->crtc = crtc; + spin_lock_init(&vtimer->interval_lock); + hrtimer_setup(&vtimer->timer, drm_vblank_timer_function, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + } else { + /* + * Timer should not be active. If it is, wait for the + * previous cancel operations to finish. + */ + while (hrtimer_active(&vtimer->timer)) + hrtimer_try_to_cancel(&vtimer->timer); + } + + drm_calc_timestamping_constants(crtc, &crtc->mode); + + spin_lock_irqsave(&vtimer->interval_lock, flags); + vtimer->interval = ns_to_ktime(vblank->framedur_ns); + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + hrtimer_start(&vtimer->timer, vtimer->interval, HRTIMER_MODE_REL); + + return 0; +} +EXPORT_SYMBOL(drm_crtc_vblank_start_timer); + +/** + * drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer + * @crtc: the CRTC + * + * Drivers should call this function from their CRTC's disable_vblank + * function to stop a vblank timer. + */ +void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + unsigned long flags; + + /* + * Calling hrtimer_cancel() can result in a deadlock with DRM's + * vblank_time_lime_lock and hrtimers' softirq_expiry_lock. So + * clear interval and indicate cancellation. The timer function + * will cancel itself on the next invocation. + */ + + spin_lock_irqsave(&vtimer->interval_lock, flags); + vtimer->interval = 0; + spin_unlock_irqrestore(&vtimer->interval_lock, flags); + + hrtimer_try_to_cancel(&vtimer->timer); +} +EXPORT_SYMBOL(drm_crtc_vblank_cancel_timer); + +/** + * drm_crtc_vblank_get_vblank_timeout - Returns the vblank timeout + * @crtc: The CRTC + * @vblank_time: Returns the next vblank timestamp + * + * The helper drm_crtc_vblank_get_vblank_timeout() returns the next vblank + * timestamp of the CRTC's vblank timer according to the timer's expiry + * time. + */ +void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time) +{ + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); + struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer; + u64 cur_count; + ktime_t cur_time; + + if (!READ_ONCE(vblank->enabled)) { + *vblank_time = ktime_get(); + return; + } + + /* + * A concurrent vblank timeout could update the expires field before + * we compare it with the vblank time. Hence we'd compare the old + * expiry time to the new vblank time; deducing the timer had already + * expired. Reread until we get consistent values from both fields. + */ + do { + cur_count = drm_crtc_vblank_count_and_time(crtc, &cur_time); + *vblank_time = READ_ONCE(vtimer->timer.node.expires); + } while (cur_count != drm_crtc_vblank_count_and_time(crtc, &cur_time)); + + if (drm_WARN_ON(crtc->dev, !ktime_compare(*vblank_time, cur_time))) + return; /* Already expired */ + + /* + * To prevent races we roll the hrtimer forward before we do any + * interrupt processing - this is how real hw works (the interrupt + * is only generated after all the vblank registers are updated) + * and what the vblank core expects. Therefore we need to always + * correct the timestamp by one frame. + */ + *vblank_time = ktime_sub(*vblank_time, vtimer->interval); +} +EXPORT_SYMBOL(drm_crtc_vblank_get_vblank_timeout); diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c new file mode 100644 index 000000000000..f94d1e706191 --- /dev/null +++ b/drivers/gpu/drm/drm_vblank_helper.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include + +/** + * DOC: overview + * + * The vblank helper library provides functions for supporting vertical + * blanking in DRM drivers. + * + * For vblank timers, several callback implementations are available. + * Drivers enable support for vblank timers by setting the vblank callbacks + * in struct &drm_crtc_funcs to the helpers provided by this library. The + * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently. + * + * Once the driver enables vblank support with drm_vblank_init(), each + * CRTC's vblank timer fires according to the programmed display mode. By + * default, the vblank timer invokes drm_crtc_handle_vblank(). Drivers with + * more specific requirements can set their own handler function in + * struct &drm_crtc_helper_funcs.handle_vblank_timeout. + */ + +/* + * VBLANK timer + */ + +/** + * drm_crtc_vblank_helper_enable_vblank_timer - Implements struct &drm_crtc_funcs.enable_vblank + * @crtc: The CRTC + * + * The helper drm_crtc_vblank_helper_enable_vblank_timer() implements + * enable_vblank of struct drm_crtc_helper_funcs for CRTCs that require + * a VBLANK timer. It sets up the timer on the first invocation. The + * started timer expires after the current frame duration. See struct + * &drm_vblank_crtc.framedur_ns. + * + * See also struct &drm_crtc_helper_funcs.enable_vblank. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc) +{ + return drm_crtc_vblank_start_timer(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_enable_vblank_timer); + +/** + * drm_crtc_vblank_helper_disable_vblank_timer - Implements struct &drm_crtc_funcs.disable_vblank + * @crtc: The CRTC + * + * The helper drm_crtc_vblank_helper_disable_vblank_timer() implements + * disable_vblank of struct drm_crtc_funcs for CRTCs that require a + * VBLANK timer. + * + * See also struct &drm_crtc_helper_funcs.disable_vblank. + */ +void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc) +{ + drm_crtc_vblank_cancel_timer(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_disable_vblank_timer); + +/** + * drm_crtc_vblank_helper_get_vblank_timestamp_from_timer - + * Implements struct &drm_crtc_funcs.get_vblank_timestamp + * @crtc: The CRTC + * @max_error: Maximum acceptable error + * @vblank_time: Returns the next vblank timestamp + * @in_vblank_irq: True is called from drm_crtc_handle_vblank() + * + * The helper drm_crtc_helper_get_vblank_timestamp_from_timer() implements + * get_vblank_timestamp of struct drm_crtc_funcs for CRTCs that require a + * VBLANK timer. It returns the timestamp according to the timer's expiry + * time. + * + * See also struct &drm_crtc_funcs.get_vblank_timestamp. + * + * Returns: + * True on success, or false otherwise. + */ +bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq) +{ + drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time); + + return true; +} +EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_from_timer); diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index ce7c7aeac887..fe32854b7ffe 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -490,6 +490,18 @@ struct drm_crtc_helper_funcs { bool in_vblank_irq, int *vpos, int *hpos, ktime_t *stime, ktime_t *etime, const struct drm_display_mode *mode); + + /** + * @handle_vblank_timeout: Handles timeouts of the vblank timer. + * + * Called by CRTC's the vblank timer on each timeout. Semantics is + * equivalient to drm_crtc_handle_vblank(). Implementations should + * invoke drm_crtc_handle_vblank() as part of processing the timeout. + * + * This callback is optional. If unset, the vblank timer invokes + * drm_crtc_handle_vblank() directly. + */ + bool (*handle_vblank_timeout)(struct drm_crtc *crtc); }; /** diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 151ab1e85b1b..ffa564d79638 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -25,6 +25,7 @@ #define _DRM_VBLANK_H_ #include +#include #include #include #include @@ -103,6 +104,28 @@ struct drm_vblank_crtc_config { bool disable_immediate; }; +/** + * struct drm_vblank_crtc_timer - vblank timer for a CRTC + */ +struct drm_vblank_crtc_timer { + /** + * @timer: The vblank's high-resolution timer + */ + struct hrtimer timer; + /** + * @interval_lock: Protects @interval + */ + spinlock_t interval_lock; + /** + * @interval: Duration between two vblanks + */ + ktime_t interval; + /** + * @crtc: The timer's CRTC + */ + struct drm_crtc *crtc; +}; + /** * struct drm_vblank_crtc - vblank tracking for a CRTC * @@ -254,6 +277,11 @@ struct drm_vblank_crtc { * cancelled. */ wait_queue_head_t work_wait_queue; + + /** + * @vblank_timer: Holds the state of the vblank timer + */ + struct drm_vblank_crtc_timer vblank_timer; }; struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc); @@ -290,6 +318,10 @@ wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc, u32 max_vblank_count); +int drm_crtc_vblank_start_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time); + /* * Helpers for struct drm_crtc_funcs */ diff --git a/include/drm/drm_vblank_helper.h b/include/drm/drm_vblank_helper.h new file mode 100644 index 000000000000..74a971d0cfba --- /dev/null +++ b/include/drm/drm_vblank_helper.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef _DRM_VBLANK_HELPER_H_ +#define _DRM_VBLANK_HELPER_H_ + +#include +#include + +struct drm_crtc; + +/* + * VBLANK timer + */ + +int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc); +void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc); +bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc, + int *max_error, + ktime_t *vblank_time, + bool in_vblank_irq); + +/** + * DRM_CRTC_VBLANK_TIMER_FUNCS - Default implementation for VBLANK timers + * + * This macro initializes struct &drm_crtc_funcs to default helpers for + * VBLANK timers. + */ +#define DRM_CRTC_VBLANK_TIMER_FUNCS \ + .enable_vblank = drm_crtc_vblank_helper_enable_vblank_timer, \ + .disable_vblank = drm_crtc_vblank_helper_disable_vblank_timer, \ + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp_from_timer + +#endif From 60918357456d39f640de25845890614caa44a30a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 26 May 2026 21:31:20 +0800 Subject: [PATCH 022/697] drm/vblank: Add CRTC helpers for simple use cases [ Upstream commit d54dbb5963bdbdf8559903fe2b2343e871adcb30 ] Implement atomic_flush, atomic_enable and atomic_disable of struct drm_crtc_helper_funcs for vblank handling. Driver with no further requirements can use these functions instead of adding their own. Also simplifies the use of vblank timers. The code has been adopted from vkms, which added the funtionality in commit 3a0709928b17 ("drm/vkms: Add vblank events simulated by hrtimers"). v3: - mention vkms (Javier) v2: - fix docs Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Tested-by: Michael Kelley Link: https://lore.kernel.org/r/20250916083816.30275-3-tzimmermann@suse.de Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_vblank_helper.c | 80 +++++++++++++++++++++++++++++ include/drm/drm_vblank_helper.h | 23 +++++++++ 2 files changed, 103 insertions(+) diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c index f94d1e706191..a04a6ba1b0ca 100644 --- a/drivers/gpu/drm/drm_vblank_helper.c +++ b/drivers/gpu/drm/drm_vblank_helper.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT +#include #include #include #include @@ -17,6 +18,12 @@ * Drivers enable support for vblank timers by setting the vblank callbacks * in struct &drm_crtc_funcs to the helpers provided by this library. The * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently. + * The driver further has to send the VBLANK event from its atomic_flush + * callback and control vblank from the CRTC's atomic_enable and atomic_disable + * callbacks. The callbacks are located in struct &drm_crtc_helper_funcs. + * The vblank helper library provides implementations of these callbacks + * for drivers without further requirements. The initializer macro + * DRM_CRTC_HELPER_VBLANK_FUNCS sets them coveniently. * * Once the driver enables vblank support with drm_vblank_init(), each * CRTC's vblank timer fires according to the programmed display mode. By @@ -25,6 +32,79 @@ * struct &drm_crtc_helper_funcs.handle_vblank_timeout. */ +/* + * VBLANK helpers + */ + +/** + * drm_crtc_vblank_atomic_flush - + * Implements struct &drm_crtc_helper_funcs.atomic_flush + * @crtc: The CRTC + * @state: The atomic state to apply + * + * The helper drm_crtc_vblank_atomic_flush() implements atomic_flush of + * struct drm_crtc_helper_funcs for CRTCs that only need to send out a + * VBLANK event. + * + * See also struct &drm_crtc_helper_funcs.atomic_flush. + */ +void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *dev = crtc->dev; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_pending_vblank_event *event; + + spin_lock_irq(&dev->event_lock); + + event = crtc_state->event; + crtc_state->event = NULL; + + if (event) { + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + } + + spin_unlock_irq(&dev->event_lock); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_flush); + +/** + * drm_crtc_vblank_atomic_enable - Implements struct &drm_crtc_helper_funcs.atomic_enable + * @crtc: The CRTC + * @state: The atomic state + * + * The helper drm_crtc_vblank_atomic_enable() implements atomic_enable + * of struct drm_crtc_helper_funcs for CRTCs the only need to enable VBLANKs. + * + * See also struct &drm_crtc_helper_funcs.atomic_enable. + */ +void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_on(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_enable); + +/** + * drm_crtc_vblank_atomic_disable - Implements struct &drm_crtc_helper_funcs.atomic_disable + * @crtc: The CRTC + * @state: The atomic state + * + * The helper drm_crtc_vblank_atomic_disable() implements atomic_disable + * of struct drm_crtc_helper_funcs for CRTCs the only need to disable VBLANKs. + * + * See also struct &drm_crtc_funcs.atomic_disable. + */ +void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + drm_crtc_vblank_off(crtc); +} +EXPORT_SYMBOL(drm_crtc_vblank_atomic_disable); + /* * VBLANK timer */ diff --git a/include/drm/drm_vblank_helper.h b/include/drm/drm_vblank_helper.h index 74a971d0cfba..fcd8a9b35846 100644 --- a/include/drm/drm_vblank_helper.h +++ b/include/drm/drm_vblank_helper.h @@ -6,8 +6,31 @@ #include #include +struct drm_atomic_state; struct drm_crtc; +/* + * VBLANK helpers + */ + +void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_state *state); +void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_state *crtc_state); + +/** + * DRM_CRTC_HELPER_VBLANK_FUNCS - Default implementation for VBLANK helpers + * + * This macro initializes struct &drm_crtc_helper_funcs to default helpers + * for VBLANK handling. + */ +#define DRM_CRTC_HELPER_VBLANK_FUNCS \ + .atomic_flush = drm_crtc_vblank_atomic_flush, \ + .atomic_enable = drm_crtc_vblank_atomic_enable, \ + .atomic_disable = drm_crtc_vblank_atomic_disable + /* * VBLANK timer */ From a0582cc923985c6b72fe871b5f7aa7c682bfc230 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 26 May 2026 21:31:21 +0800 Subject: [PATCH 023/697] drm/vkms: Convert to DRM's vblank timer [ Upstream commit 02e2681ffe1addde1fc8c35d05657b16bfa79613 ] Replace vkms' vblank timer with the DRM implementation. The DRM code is identical in concept, but differs in implementation. Vblank timers are covered in vblank helpers and initializer macros, so remove the corresponding hrtimer in struct vkms_output. The vblank timer calls vkms' custom timeout code via handle_vblank_timeout in struct drm_crtc_helper_funcs. Signed-off-by: Thomas Zimmermann Tested-by: Louis Chauvet Reviewed-by: Louis Chauvet Reviewed-by: Javier Martinez Canillas Link: https://lore.kernel.org/r/20250916083816.30275-4-tzimmermann@suse.de Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Sasha Levin --- drivers/gpu/drm/vkms/vkms_crtc.c | 83 +++----------------------------- drivers/gpu/drm/vkms/vkms_drv.h | 2 - 2 files changed, 7 insertions(+), 78 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index e60573e0f3e9..bd79f24686dc 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -7,25 +7,18 @@ #include #include #include +#include #include "vkms_drv.h" -static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) +static bool vkms_crtc_handle_vblank_timeout(struct drm_crtc *crtc) { - struct vkms_output *output = container_of(timer, struct vkms_output, - vblank_hrtimer); - struct drm_crtc *crtc = &output->crtc; + struct vkms_output *output = drm_crtc_to_vkms_output(crtc); struct vkms_crtc_state *state; - u64 ret_overrun; bool ret, fence_cookie; fence_cookie = dma_fence_begin_signalling(); - ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer, - output->period_ns); - if (ret_overrun != 1) - pr_warn("%s: vblank timer overrun\n", __func__); - spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) @@ -57,55 +50,6 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) dma_fence_end_signalling(fence_cookie); - return HRTIMER_RESTART; -} - -static int vkms_enable_vblank(struct drm_crtc *crtc) -{ - struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); - struct vkms_output *out = drm_crtc_to_vkms_output(crtc); - - hrtimer_setup(&out->vblank_hrtimer, &vkms_vblank_simulate, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); - out->period_ns = ktime_set(0, vblank->framedur_ns); - hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL); - - return 0; -} - -static void vkms_disable_vblank(struct drm_crtc *crtc) -{ - struct vkms_output *out = drm_crtc_to_vkms_output(crtc); - - hrtimer_cancel(&out->vblank_hrtimer); -} - -static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc, - int *max_error, ktime_t *vblank_time, - bool in_vblank_irq) -{ - struct vkms_output *output = drm_crtc_to_vkms_output(crtc); - struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); - - if (!READ_ONCE(vblank->enabled)) { - *vblank_time = ktime_get(); - return true; - } - - *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires); - - if (WARN_ON(*vblank_time == vblank->time)) - return true; - - /* - * To prevent races we roll the hrtimer forward before we do any - * interrupt processing - this is how real hw works (the interrupt is - * only generated after all the vblank registers are updated) and what - * the vblank core expects. Therefore we need to always correct the - * timestampe by one frame. - */ - *vblank_time -= output->period_ns; - return true; } @@ -159,9 +103,7 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = { .reset = vkms_atomic_crtc_reset, .atomic_duplicate_state = vkms_atomic_crtc_duplicate_state, .atomic_destroy_state = vkms_atomic_crtc_destroy_state, - .enable_vblank = vkms_enable_vblank, - .disable_vblank = vkms_disable_vblank, - .get_vblank_timestamp = vkms_get_vblank_timestamp, + DRM_CRTC_VBLANK_TIMER_FUNCS, .get_crc_sources = vkms_get_crc_sources, .set_crc_source = vkms_set_crc_source, .verify_crc_source = vkms_verify_crc_source, @@ -213,18 +155,6 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc, return 0; } -static void vkms_crtc_atomic_enable(struct drm_crtc *crtc, - struct drm_atomic_state *state) -{ - drm_crtc_vblank_on(crtc); -} - -static void vkms_crtc_atomic_disable(struct drm_crtc *crtc, - struct drm_atomic_state *state) -{ - drm_crtc_vblank_off(crtc); -} - static void vkms_crtc_atomic_begin(struct drm_crtc *crtc, struct drm_atomic_state *state) __acquires(&vkms_output->lock) @@ -265,8 +195,9 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = { .atomic_check = vkms_crtc_atomic_check, .atomic_begin = vkms_crtc_atomic_begin, .atomic_flush = vkms_crtc_atomic_flush, - .atomic_enable = vkms_crtc_atomic_enable, - .atomic_disable = vkms_crtc_atomic_disable, + .atomic_enable = drm_crtc_vblank_atomic_enable, + .atomic_disable = drm_crtc_vblank_atomic_disable, + .handle_vblank_timeout = vkms_crtc_handle_vblank_timeout, }; struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *primary, diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 8013c31efe3b..fb9711e1c6fb 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -215,8 +215,6 @@ struct vkms_output { struct drm_crtc crtc; struct drm_writeback_connector wb_connector; struct drm_encoder wb_encoder; - struct hrtimer vblank_hrtimer; - ktime_t period_ns; struct workqueue_struct *composer_workq; spinlock_t lock; From ed39ecd3a96cdf94589aef46f2f711cf93baa0d3 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 26 May 2026 21:31:22 +0800 Subject: [PATCH 024/697] drm/atomic: Increase timeout in drm_atomic_helper_wait_for_vblanks() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 79ae8510b5b81b9500370f89c619b50ca9c0990f ] Increase the timeout for vblank events from 100 ms to 1000 ms. This is the same fix as in commit f050da08a4ed ("drm/vblank: Increase timeout in drm_wait_one_vblank()") for another vblank timeout. After merging generic DRM vblank timers [1] and converting several DRM drivers for virtual hardware, these drivers synchronize their vblank events to the display refresh rate. This can trigger timeouts within the DRM framework. Signed-off-by: Thomas Zimmermann Link: https://lore.kernel.org/dri-devel/20250904145806.430568-1-tzimmermann@suse.de/ # [1] Reported-by: syzbot+fcede535e7eb57cf5b43@syzkaller.appspotmail.com Closes: https://lore.kernel.org/dri-devel/69381d6c.050a0220.4004e.0017.GAE@google.com/ Reviewed-by: Ville Syrjälä Fixes: 74afeb812850 ("drm/vblank: Add vblank timer") Link: https://patch.msgid.link/20251209143325.102056-1-tzimmermann@suse.de Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_atomic_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bbec1c184f65..b06a5cba5295 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1913,7 +1913,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, ret = wait_event_timeout(dev->vblank[i].queue, state->crtcs[i].last_vblank_count != drm_crtc_vblank_count(crtc), - msecs_to_jiffies(100)); + msecs_to_jiffies(1000)); WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n", crtc->base.id, crtc->name); From 141ffb83abe97db88df8822c82cd53ae5e38221a Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 26 May 2026 21:31:23 +0800 Subject: [PATCH 025/697] drm/vblank: Fix kernel docs for vblank timer [ Upstream commit 3946d3ba99342f3b9996e621f05e7003d4308171 ] Fix documentation for drm_crtc_vblank_start_timer(), which referred to drm_crtc_vblank_cancel_timer(). Signed-off-by: Thomas Zimmermann Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/dri-devel/20251106152201.6f248c09@canb.auug.org.au/ Fixes: 74afeb812850 ("drm/vblank: Add vblank timer") Cc: Thomas Zimmermann Cc: Louis Chauvet Cc: Javier Martinez Canillas Cc: David Airlie Cc: Simona Vetter Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: dri-devel@lists.freedesktop.org Reviewed-by: Louis Chauvet Link: https://patch.msgid.link/20251106073207.11192-1-tzimmermann@suse.de Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Sasha Levin --- drivers/gpu/drm/drm_vblank.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 61e211fd3c9c..451ec9620226 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -2258,7 +2258,7 @@ int drm_crtc_vblank_start_timer(struct drm_crtc *crtc) EXPORT_SYMBOL(drm_crtc_vblank_start_timer); /** - * drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer + * drm_crtc_vblank_cancel_timer - Cancels the given CRTC's vblank timer * @crtc: the CRTC * * Drivers should call this function from their CRTC's disable_vblank From 48fa96538bd2868034d33429e4565fda384d0736 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 20 May 2026 15:05:04 +0200 Subject: [PATCH 026/697] sysfs: don't remove existing directory on update failure commit 237557b8a81ab948e8332f7c0058e758f081c0a3 upstream. When sysfs_update_group() is called for a named group and create_files() fails (e.g. -ENOMEM), internal_create_group() calls kernfs_remove(kn) on the group directory. In the update path, kn was obtained via kernfs_find_and_get() and refers to a directory that already existed before this call. Removing it silently destroys a sysfs group that the caller did not create. Only remove the directory if we created it ourselves. On update failure the directory remains as it is left empty by remove_files() inside create_files(), but can be repopulated by a retry. Cc: Rajat Jain Fixes: c855cf2759d2 ("sysfs: Fix internal_create_group() for named group updates") Cc: stable Assisted-by: gkh_clanker_t1000 Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Danilo Krummrich Link: https://patch.msgid.link/2026052003-uniquely-hastily-c093@gregkh Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index e142bac4f9f8..68c75913c978 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -182,7 +182,7 @@ static int internal_create_group(struct kobject *kobj, int update, kernfs_get(kn); error = create_files(kn, kobj, uid, gid, grp, update); if (error) { - if (grp->name) + if (grp->name && !update) kernfs_remove(kn); } kernfs_put(kn); From 302e02f9ba49f81418ec2a749ae6f5cac1d424e9 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Sun, 26 Apr 2026 10:36:12 -0700 Subject: [PATCH 027/697] mm/damon/sysfs-schemes: call missing mem_cgroup_iter_break() commit d4e7b5c4cc353f154d5ab8bb2e1ce7714d77a6e9 upstream. damon_sysfs_memcg_path_to_id() breaks mem_cgroup_iter() loop without calling mem_cgroup_iter_break(). This leaks the cgroup reference. Fix the issue by calling mem_cgroup_iter_break() before the break. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260426173625.86521-1-sj@kernel.org Link: https://lore.kernel.org/20260423004148.74722-1-sj@kernel.org [1] Fixes: 29cbb9a13f05 ("mm/damon/sysfs-schemes: implement scheme filters") Signed-off-by: SeongJae Park Cc: # 6.3.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/sysfs-schemes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index ca82dc78f0b9..a7be6ea812e4 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -2444,6 +2444,7 @@ static int damon_sysfs_memcg_path_to_id(char *memcg_path, unsigned short *id) if (damon_sysfs_memcg_path_eq(memcg, path, memcg_path)) { *id = mem_cgroup_id(memcg); found = true; + mem_cgroup_iter_break(NULL, memcg); break; } } From cd5c1b75d2f454f625d7dc55bd3ae21d0855f6ad Mon Sep 17 00:00:00 2001 From: Jeremy Laratro Date: Wed, 13 May 2026 08:26:16 +0900 Subject: [PATCH 028/697] ksmbd: fix null pointer dereference in compare_guid_key() commit 4b83cbc4c15f09b000cc06f033f64b0824b6dc87 upstream. session_fd_check() walks the per-inode m_op_list during durable-handle session teardown and sets op->conn = NULL for every opinfo whose conn matched the closing session's connection. The matching opinfo, however, stays linked in its per-ClientGuid lease_table_list entry's lb->lease_list because destroy_lease_table() only runs on full TCP-connection teardown, not on SESSION_LOGOFF. If the same TCP connection then negotiates a fresh session with the same ClientGuid (ClientGuid is bound to NEGOTIATE, not the session, and is unchanged across LOGOFF + SETUP) and issues a SMB2 CREATE with a lease context on a different inode, find_same_lease_key() walks lb->lease_list, reaches the stale opinfo, and calls compare_guid_key(), which unconditionally dereferences opinfo->conn->ClientGUID. The conn pointer is NULL and the kernel panics. Reproducer requires only a successful SMB2 SESSION_SETUP and a share configured with 'durable handles = yes'. KASAN report on mainline 70390501d194: general protection fault, probably for non-canonical address 0xdffffc0000000069: 0000 [#1] SMP KASAN PTI KASAN: null-ptr-deref in range [0x0000000000000348-0x000000000000034f] Workqueue: ksmbd-io handle_ksmbd_work RIP: 0010:bcmp+0x5b/0x230 Call Trace: compare_guid_key+0x4b/0xd0 find_same_lease_key+0x324/0x690 smb2_open+0x6aea/0x8e60 handle_ksmbd_work+0x796/0xee0 ... Faulting address 0x348 is the offset of ClientGUID within struct ksmbd_conn, confirming opinfo->conn was NULL. Read opinfo->conn once and bail out if it has been cleared by a concurrent session_fd_check(). A half-detached opinfo cannot be the owner of an active lease, so returning 0 is the correct match result. Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") Cc: stable@vger.kernel.org Signed-off-by: Jeremy Laratro Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/oplock.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index bbb2cb3782d0..a84c01bceb8b 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -484,8 +484,12 @@ static inline int compare_guid_key(struct oplock_info *opinfo, const char *guid1, const char *key1) { const char *guid2, *key2; + struct ksmbd_conn *conn; - guid2 = opinfo->conn->ClientGUID; + conn = READ_ONCE(opinfo->conn); + if (!conn) + return 0; + guid2 = conn->ClientGUID; key2 = opinfo->o_lease->lease_key; if (!memcmp(guid1, guid2, SMB2_CLIENT_GUID_SIZE) && !memcmp(key1, key2, SMB2_LEASE_KEY_SIZE)) From 0e198f09cb2a554c04de0fea4e790f1250a943ca Mon Sep 17 00:00:00 2001 From: Ferry Meng Date: Mon, 11 May 2026 21:18:16 +0800 Subject: [PATCH 029/697] ksmbd: fix SID memory leak in set_posix_acl_entries_dacl() on overflow commit af92ee994cc7f7e83a41c2025f32257a2f82a7ef upstream. Commit 299f962c0b02 ("ksmbd: use check_add_overflow() to prevent u16 DACL size overflow") added check_add_overflow() guards that break out of the ACE-building loops in set_posix_acl_entries_dacl() when the accumulated DACL size would wrap past 65535. However, each iteration allocates a struct smb_sid via kmalloc_obj() at the top of the loop and relies on the kfree(sid) call at the end of the loop body (the 'pass_same_sid' label in the first loop, and the explicit kfree at the tail of the second loop) to release it. The newly introduced 'break' statements bypass those kfree() calls, leaking the sid buffer every time an overflow is detected. A malicious or malformed file with enough POSIX ACL entries to trip the overflow check will leak one or more struct smb_sid allocations on every request that touches the file's DACL, providing a trivial kernel memory exhaustion vector. Free sid before breaking out of the loops to plug the leak. Fixes: 299f962c0b02 ("ksmbd: use check_add_overflow() to prevent u16 DACL size overflow") Cc: stable@vger.kernel.org Signed-off-by: Ferry Meng Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smbacl.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index a0e0dc56c730..4ea489a79afa 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -643,8 +643,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ntace = (struct smb_ace *)((char *)pndace + *size); ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags, pace->e_perm, 0777); - if (check_add_overflow(*size, ace_sz, size)) + if (check_add_overflow(*size, ace_sz, size)) { + kfree(sid); break; + } (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= @@ -655,8 +657,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ntace = (struct smb_ace *)((char *)pndace + *size); ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x03, pace->e_perm, 0777); - if (check_add_overflow(*size, ace_sz, size)) + if (check_add_overflow(*size, ace_sz, size)) { + kfree(sid); break; + } (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= @@ -698,8 +702,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, ntace = (struct smb_ace *)((char *)pndace + *size); ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b, pace->e_perm, 0777); - if (check_add_overflow(*size, ace_sz, size)) + if (check_add_overflow(*size, ace_sz, size)) { + kfree(sid); break; + } (*num_aces)++; if (pace->e_tag == ACL_USER) ntace->access_req |= From 18d8db24b0a5b7be4829238dd4022236df02d421 Mon Sep 17 00:00:00 2001 From: Junyi Liu Date: Tue, 19 May 2026 16:12:04 +0900 Subject: [PATCH 030/697] ksmbd: validate SID in parent security descriptor during ACL inheritance commit 69f030cf95488ae1186c72ac8c66fd279664ea7f upstream. Introduce smb_validate_ntsd_sid() helper to safely validate Owner SID and Group SID inside the NT Security Descriptor (smb_ntsd) retrieved from the parent directory. Cc: stable@vger.kernel.org Signed-off-by: Junyi Liu Signed-off-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smbacl.c | 66 ++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 4ea489a79afa..6c4f9c8c7f13 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1096,6 +1096,40 @@ static int smb_append_inherited_ace(struct smb_ace **ace, int *nt_size, return 0; } +static int smb_validate_ntsd_sid(struct smb_ntsd *pntsd, size_t pntsd_size, + unsigned int sid_offset, struct smb_sid **sid, + size_t *sid_size) +{ + size_t sid_end; + + *sid = NULL; + *sid_size = 0; + + if (!sid_offset) + return 0; + + if (sid_offset < sizeof(struct smb_ntsd) || + check_add_overflow(sid_offset, (size_t)CIFS_SID_BASE_SIZE, + &sid_end) || + sid_end > pntsd_size) + return -EINVAL; + + *sid = (struct smb_sid *)((char *)pntsd + sid_offset); + if ((*sid)->num_subauth > SID_MAX_SUB_AUTHORITIES) + return -EINVAL; + + if (check_add_overflow((size_t)CIFS_SID_BASE_SIZE, + sizeof(__le32) * (size_t)(*sid)->num_subauth, + &sid_end)) + return -EINVAL; + + if (sid_offset > pntsd_size || sid_end > pntsd_size - sid_offset) + return -EINVAL; + + *sid_size = sid_end; + return 0; +} + int smb_inherit_dacl(struct ksmbd_conn *conn, const struct path *path, unsigned int uid, unsigned int gid) @@ -1108,28 +1142,28 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct dentry *parent = path->dentry->d_parent; struct mnt_idmap *idmap = mnt_idmap(path->mnt); int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size; - int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size; + int rc = 0, pntsd_type, ppntsd_size, acl_len, aces_size; unsigned int dacloffset; size_t dacl_struct_end; u16 num_aces, ace_cnt = 0; char *aces_base; bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); - pntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap, + ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap, parent, &parent_pntsd); - if (pntsd_size <= 0) + if (ppntsd_size <= 0) return -ENOENT; dacloffset = le32_to_cpu(parent_pntsd->dacloffset); if (!dacloffset || check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) || - dacl_struct_end > (size_t)pntsd_size) { + dacl_struct_end > (size_t)ppntsd_size) { rc = -EINVAL; goto free_parent_pntsd; } parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); - acl_len = pntsd_size - dacloffset; + acl_len = ppntsd_size - dacloffset; num_aces = le16_to_cpu(parent_pdacl->num_aces); pntsd_type = le16_to_cpu(parent_pntsd->type); pdacl_size = le16_to_cpu(parent_pdacl->size); @@ -1243,19 +1277,19 @@ int smb_inherit_dacl(struct ksmbd_conn *conn, struct smb_ntsd *pntsd; struct smb_acl *pdacl; struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL; - int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; + size_t powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size; size_t pntsd_alloc_size; - if (parent_pntsd->osidoffset) { - powner_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->osidoffset)); - powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4); - } - if (parent_pntsd->gsidoffset) { - pgroup_sid = (struct smb_sid *)((char *)parent_pntsd + - le32_to_cpu(parent_pntsd->gsidoffset)); - pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4); - } + rc = smb_validate_ntsd_sid(parent_pntsd, ppntsd_size, + le32_to_cpu(parent_pntsd->osidoffset), + &powner_sid, &powner_sid_size); + if (rc) + goto free_aces_base; + rc = smb_validate_ntsd_sid(parent_pntsd, ppntsd_size, + le32_to_cpu(parent_pntsd->gsidoffset), + &pgroup_sid, &pgroup_sid_size); + if (rc) + goto free_aces_base; if (check_add_overflow(sizeof(struct smb_ntsd), (size_t)powner_sid_size, From 6827647fd2dcf4e7f355478a42e82f51e0b5344c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 18 May 2026 10:31:11 +0200 Subject: [PATCH 031/697] regulator: tps65219: fix irq_data.rdev not being assigned commit f9b2d3b703d13df50c630997dfdc25648e96db0d upstream. Commit 64a6b577490c ("regulator: tps65219: Remove debugging helper function") removed the tps65219_get_rdev_by_name() helper along with the irq_data.rdev assignment that depended on it. This left irq_data.rdev uninitialized for all IRQs, causing undefined behavior when regulator_notifier_call_chain() is called from the IRQ handler: Internal error: Oops: 0000000096000004 pc : regulator_notifier_call_chain lr : tps65219_regulator_irq_handler Call trace: regulator_notifier_call_chain tps65219_regulator_irq_handler handle_nested_irq regmap_irq_thread irq_thread_fn irq_thread kthread ret_from_fork Instead of restoring a dedicated lookup array, restructure the probe function to combine regulator registration with IRQ registration in the same loop. This way the rdev returned by devm_regulator_register() is naturally available for assigning to irq_data.rdev without any auxiliary data structure. Non-regulator IRQs (SENSOR, TIMEOUT) that don't correspond to any registered regulator are registered with rdev=NULL, and the IRQ handler is protected with a NULL check to avoid crashing. Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/aBDSTxALaOc-PD7X@gaggiata.pivistrello.it/ Reported-by: Francesco Dolcini Fixes: 64a6b577490c ("regulator: tps65219: Remove debugging helper function") Signed-off-by: Alexander Sverdlin Link: https://patch.msgid.link/20260518083113.2063368-1-alexander.sverdlin@siemens.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/regulator/tps65219-regulator.c | 135 +++++++++++++++++-------- 1 file changed, 95 insertions(+), 40 deletions(-) diff --git a/drivers/regulator/tps65219-regulator.c b/drivers/regulator/tps65219-regulator.c index d77ca486879f..324c3a33af8a 100644 --- a/drivers/regulator/tps65219-regulator.c +++ b/drivers/regulator/tps65219-regulator.c @@ -346,8 +346,9 @@ static irqreturn_t tps65219_regulator_irq_handler(int irq, void *data) return IRQ_HANDLED; } - regulator_notifier_call_chain(irq_data->rdev, - irq_data->type->event, NULL); + if (irq_data->rdev) + regulator_notifier_call_chain(irq_data->rdev, + irq_data->type->event, NULL); dev_err(irq_data->dev, "Error IRQ trap %s for %s\n", irq_data->type->event_name, irq_data->type->regulator_name); @@ -398,14 +399,65 @@ static struct tps65219_chip_data chip_info_table[] = { }, }; -static int tps65219_regulator_probe(struct platform_device *pdev) +static bool tps65219_is_regulator_name(const struct tps65219_chip_data *pmic, + const char *name) +{ + int i; + + for (i = 0; i < pmic->common_rdesc_size; i++) + if (!strcmp(pmic->common_rdesc[i].name, name)) + return true; + for (i = 0; i < pmic->rdesc_size; i++) + if (!strcmp(pmic->rdesc[i].name, name)) + return true; + return false; +} + +static int tps65219_register_irqs(struct platform_device *pdev, + struct tps65219 *tps, + struct regulator_dev *rdev, + struct tps65219_regulator_irq_type *irq_types, + int nirqs, + const char *regulator_name) { struct tps65219_regulator_irq_data *irq_data; + int i, irq, error; + + for (i = 0; i < nirqs; i++) { + if (strcmp(irq_types[i].regulator_name, regulator_name)) + continue; + + irq = platform_get_irq_byname(pdev, irq_types[i].irq_name); + if (irq < 0) + return -EINVAL; + + irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL); + if (!irq_data) + return -ENOMEM; + + irq_data->dev = tps->dev; + irq_data->type = &irq_types[i]; + irq_data->rdev = rdev; + + error = devm_request_threaded_irq(tps->dev, irq, NULL, + tps65219_regulator_irq_handler, + IRQF_ONESHOT, + irq_types[i].irq_name, + irq_data); + if (error) + return dev_err_probe(tps->dev, error, + "Failed to request %s IRQ %d\n", + irq_types[i].irq_name, irq); + } + return 0; +} + +static int tps65219_regulator_probe(struct platform_device *pdev) +{ struct tps65219_regulator_irq_type *irq_type; struct tps65219_chip_data *pmic; struct regulator_dev *rdev; int error; - int irq; int i; struct tps65219 *tps = dev_get_drvdata(pdev->dev.parent); @@ -425,6 +477,19 @@ static int tps65219_regulator_probe(struct platform_device *pdev) return dev_err_probe(tps->dev, PTR_ERR(rdev), "Failed to register %s regulator\n", pmic->common_rdesc[i].name); + + error = tps65219_register_irqs(pdev, tps, rdev, + pmic->common_irq_types, + pmic->common_irq_size, + pmic->common_rdesc[i].name); + if (error) + return error; + error = tps65219_register_irqs(pdev, tps, rdev, + pmic->irq_types, + pmic->dev_irq_size, + pmic->common_rdesc[i].name); + if (error) + return error; } for (i = 0; i < pmic->rdesc_size; i++) { @@ -434,52 +499,42 @@ static int tps65219_regulator_probe(struct platform_device *pdev) return dev_err_probe(tps->dev, PTR_ERR(rdev), "Failed to register %s regulator\n", pmic->rdesc[i].name); + + error = tps65219_register_irqs(pdev, tps, rdev, + pmic->common_irq_types, + pmic->common_irq_size, + pmic->rdesc[i].name); + if (error) + return error; + error = tps65219_register_irqs(pdev, tps, rdev, + pmic->irq_types, + pmic->dev_irq_size, + pmic->rdesc[i].name); + if (error) + return error; } + /* Register non-regulator IRQs (TIMEOUT, SENSOR) with rdev=NULL */ for (i = 0; i < pmic->common_irq_size; ++i) { irq_type = &pmic->common_irq_types[i]; - irq = platform_get_irq_byname(pdev, irq_type->irq_name); - if (irq < 0) - return -EINVAL; - - irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL); - if (!irq_data) - return -ENOMEM; - - irq_data->dev = tps->dev; - irq_data->type = irq_type; - error = devm_request_threaded_irq(tps->dev, irq, NULL, - tps65219_regulator_irq_handler, - IRQF_ONESHOT, - irq_type->irq_name, - irq_data); + if (tps65219_is_regulator_name(pmic, irq_type->regulator_name)) + continue; + error = tps65219_register_irqs(pdev, tps, NULL, + irq_type, 1, + irq_type->regulator_name); if (error) - return dev_err_probe(tps->dev, error, - "Failed to request %s IRQ %d\n", - irq_type->irq_name, irq); + return error; } for (i = 0; i < pmic->dev_irq_size; ++i) { irq_type = &pmic->irq_types[i]; - irq = platform_get_irq_byname(pdev, irq_type->irq_name); - if (irq < 0) - return -EINVAL; - - irq_data = devm_kmalloc(tps->dev, sizeof(*irq_data), GFP_KERNEL); - if (!irq_data) - return -ENOMEM; - - irq_data->dev = tps->dev; - irq_data->type = irq_type; - error = devm_request_threaded_irq(tps->dev, irq, NULL, - tps65219_regulator_irq_handler, - IRQF_ONESHOT, - irq_type->irq_name, - irq_data); + if (tps65219_is_regulator_name(pmic, irq_type->regulator_name)) + continue; + error = tps65219_register_irqs(pdev, tps, NULL, + irq_type, 1, + irq_type->regulator_name); if (error) - return dev_err_probe(tps->dev, error, - "Failed to request %s IRQ %d\n", - irq_type->irq_name, irq); + return error; } return 0; From a8d17d22db591099519a89f14dd24810daba74c3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 20:11:50 -0400 Subject: [PATCH 032/697] smb: client: require net admin for CIFS SWN netlink commit d1ebfce2c1d161186a82e77590bf7da2ea1bce91 upstream. CIFS_GENL_CMD_SWN_NOTIFY is the userspace witness-notify command. The intended sender is the cifs.witness helper, but the generic-netlink operation currently has no capability flag, so any local process can send RESOURCE_CHANGE or CLIENT_MOVE notifications to the in-kernel witness handler. The same family exposes CIFS_GENL_MCGRP_SWN without multicast-group capability flags. Register messages sent to that group include the witness registration id and, for NTLM-authenticated mounts, the username, domain, and password attributes copied from the CIFS session. An unprivileged local process should not be able to join that group and receive those messages. Require CAP_NET_ADMIN for incoming SWN_NOTIFY commands with GENL_ADMIN_PERM, and require CAP_NET_ADMIN over the network namespace for joining the SWN multicast group with GENL_MCAST_CAP_NET_ADMIN. The cifs.witness service runs with the privileges needed for both operations. Fixes: fed979a7e082 ("cifs: Set witness notification handler for messages from userspace daemon") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/netlink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/smb/client/netlink.c b/fs/smb/client/netlink.c index 147d9409252c..0dd10913c37a 100644 --- a/fs/smb/client/netlink.c +++ b/fs/smb/client/netlink.c @@ -33,13 +33,17 @@ static const struct nla_policy cifs_genl_policy[CIFS_GENL_ATTR_MAX + 1] = { static const struct genl_ops cifs_genl_ops[] = { { .cmd = CIFS_GENL_CMD_SWN_NOTIFY, + .flags = GENL_ADMIN_PERM, .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, .doit = cifs_swn_notify, }, }; static const struct genl_multicast_group cifs_genl_mcgrps[] = { - [CIFS_GENL_MCGRP_SWN] = { .name = CIFS_GENL_MCGRP_SWN_NAME }, + [CIFS_GENL_MCGRP_SWN] = { + .name = CIFS_GENL_MCGRP_SWN_NAME, + .flags = GENL_MCAST_CAP_NET_ADMIN, + }, }; struct genl_family cifs_genl_family = { From bf4ebdb19ff9b3cdf992b50715fe61633327416a Mon Sep 17 00:00:00 2001 From: Henrique Carvalho Date: Thu, 14 May 2026 20:18:25 -0300 Subject: [PATCH 033/697] smb: client: protect tc_count increment in smb2_find_smb_sess_tcon_unlocked() commit 4d8690dace005a38e6dbde9ecce2da3ad85c7c41 upstream. Commit 96c4af418586 ("cifs: Fix locking usage for tcon fields") refactored cifs code to change cifs_tcp_ses_lock for tc_lock around tc_count changes. There was missing lock around tc_count increment inside smb2_find_smb_sess_tcon_unlocked(). Cc: stable@vger.kernel.org Fixes: 96c4af418586 ("cifs: Fix locking usage for tcon fields") Reviewed-by: Shyam Prasad N Signed-off-by: Henrique Carvalho Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2transport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c index db98ced541ba..7483b29367d5 100644 --- a/fs/smb/client/smb2transport.c +++ b/fs/smb/client/smb2transport.c @@ -176,7 +176,9 @@ smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid) list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { if (tcon->tid != tid) continue; + spin_lock(&tcon->tc_lock); ++tcon->tc_count; + spin_unlock(&tcon->tc_lock); trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, netfs_trace_tcon_ref_get_find_sess_tcon); return tcon; From d65104a4a81573994be43718f10e1c18110b9e07 Mon Sep 17 00:00:00 2001 From: Jeremy Erazo Date: Fri, 15 May 2026 19:31:41 +0000 Subject: [PATCH 034/697] smb: client: use data_len for SMB2 READ encrypted folioq copy commit d4d76c9ee1997cc8c977a63f6c43551c253c1066 upstream. In handle_read_data() the encrypted/folioq branch (buf_len <= data_offset, reached via receive_encrypted_read for transform PDUs > CIFSMaxBufSize + MAX_HEADER_SIZE) copies the READ payload using buffer_len rather than data_len: rdata->result = cifs_copy_folioq_to_iter(buffer, buffer_len, cur_off, &rdata->subreq.io_iter); ... rdata->got_bytes = buffer_len; buffer_len comes from the SMB3 transform header OriginalMessageSize field (OriginalMessageSize - read_rsp_size); it represents the size of the decrypted message after the SMB2 header. data_len comes from the SMB2 READ response DataLength field; it represents the actual READ payload size and may be smaller than buffer_len when the decrypted message contains padding or other trailing bytes after the READ payload. The existing check `data_len > buffer_len - pad_len` only enforces an upper bound, so a server that emits OriginalMessageSize larger than read_rsp_size + pad_len + data_len passes the check and the kernel copies buffer_len bytes per response, ignoring the server-asserted DataLength. Two observable failures with a crafted server (DataLength=4, buffer_len=20000): - the kernel returns 20000 bytes per sub-request to userspace and sets got_bytes = buffer_len, even though the response claimed only 4 bytes of payload; - on a partial netfs sub-request whose iterator is sized to data_len, the over-large copy_folio_to_iter() short-reads, cifs_copy_folioq_to_iter() returns -EIO via the n != len path, and the entire netfs read collapses to -EIO even though the leading sub-requests succeeded. Use data_len for the copy length and for got_bytes so the kernel honours the server-asserted READ payload size. For well-formed servers (where buffer_len == pad_len + data_len) the change is behaviour-equivalent. Cc: stable@vger.kernel.org Signed-off-by: Jeremy Erazo Acked-by: David Howells Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index eed3a71171c0..e1a9e89cb85f 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4842,7 +4842,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, } /* Copy the data to the output I/O iterator. */ - rdata->result = cifs_copy_folioq_to_iter(buffer, buffer_len, + rdata->result = cifs_copy_folioq_to_iter(buffer, data_len, cur_off, &rdata->subreq.io_iter); if (rdata->result != 0) { if (is_offloaded) @@ -4851,7 +4851,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, dequeue_mid(mid, rdata->result); return 0; } - rdata->got_bytes = buffer_len; + rdata->got_bytes = data_len; } else if (buf_len >= data_offset + data_len) { /* read response payload is in buf */ From 9803e75c9813a7cbebb258e47719ed9055b708ef Mon Sep 17 00:00:00 2001 From: ChenXiaoSong Date: Mon, 18 May 2026 15:23:22 +0000 Subject: [PATCH 035/697] smb/server: promote S_DEL_ON_CLS to S_DEL_PENDING when close commit 4ec9c8e023c79f613fe4d5ad8cc737112efb2e44 upstream. Reproducer: 1. server: systemctl start ksmbd 2. client: mount -t cifs //${server_ip}/export /mnt 3. client: C program: openat(AT_FDCWD, "/mnt", O_RDWR | O_TMPFILE, 0600) Do not treat `FILE_DELETE_ON_CLOSE_LE` as delete pending while files remain open. This patch fixes xfstests generic/004. Cc: stable@vger.kernel.org Link: https://chenxiaosong.com/en/smb-xfstests-generic-004.html Co-developed-by: Huiwen He Signed-off-by: Huiwen He Signed-off-by: ChenXiaoSong Tested-by: Steve French Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/vfs_cache.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index a8fed467e9b6..7293b7effbc1 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -118,7 +118,7 @@ int ksmbd_query_inode_status(struct dentry *dentry) return ret; down_read(&ci->m_lock); - if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)) + if (ci->m_flags & S_DEL_PENDING) ret = KSMBD_INODE_STATUS_PENDING_DELETE; else ret = KSMBD_INODE_STATUS_OK; @@ -134,7 +134,7 @@ bool ksmbd_inode_pending_delete(struct ksmbd_file *fp) int ret; down_read(&ci->m_lock); - ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS)); + ret = (ci->m_flags & S_DEL_PENDING); up_read(&ci->m_lock); return ret; @@ -302,12 +302,20 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp) } } + down_write(&ci->m_lock); + /* Promote S_DEL_ON_CLS to S_DEL_PENDING when close */ + if (ci->m_flags & S_DEL_ON_CLS) { + ci->m_flags &= ~S_DEL_ON_CLS; + ci->m_flags |= S_DEL_PENDING; + } + up_write(&ci->m_lock); + if (atomic_dec_and_test(&ci->m_count)) { bool do_unlink = false; down_write(&ci->m_lock); - if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) { - ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING); + if (ci->m_flags & S_DEL_PENDING) { + ci->m_flags &= ~S_DEL_PENDING; do_unlink = true; } up_write(&ci->m_lock); From ca560f7566df7e2826c2999e959e6b94eb938f76 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Fri, 15 May 2026 15:11:48 -0700 Subject: [PATCH 036/697] hwmon: (pmbus/adm1266) widen blackbox-info buffer to I2C_SMBUS_BLOCK_MAX commit eee213daa1e1b402eb631bcd1b8c5aa340a6b081 upstream. adm1266_nvmem_read_blackbox() declares a 5-byte stack buffer and passes it to i2c_smbus_read_block_data() to retrieve the 4-byte BLACKBOX_INFO response. i2c_smbus_read_block_data() does not honour caller buffer sizes -- it memcpy()s data.block[0] bytes from the SMBus transaction (where data.block[0] is the length byte returned by the slave device, up to I2C_SMBUS_BLOCK_MAX = 32): memcpy(values, &data.block[1], data.block[0]); If the device returns any block length above 5, the call overflows the caller's 5-byte stack buffer before the post-call if (ret != 4) return -EIO; check has a chance to reject the response. Widen the local buffer to I2C_SMBUS_BLOCK_MAX so the helper has room for any well-formed SMBus block response, matching the convention used by the other i2c_smbus_read_block_data() callers in this driver. Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-2-1c1ea1349cfe@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index d90f8f80be8e..5348feefd1e6 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -349,7 +349,7 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) { int record_count; char index; - u8 buf[5]; + u8 buf[I2C_SMBUS_BLOCK_MAX]; int ret; ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, buf); From cba8dab72e9b480f6bc07d83974afb1648c6573f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 19 May 2026 00:32:15 -0300 Subject: [PATCH 037/697] ALSA: ua101: Reject too-short USB descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b59d5c51bb328a60749b4dd5fe7e649bfb4089b4 upstream. find_format_descriptor() walks the class-specific interface extras by advancing with bLength. It rejects descriptors that extend past the remaining buffer, but it does not reject descriptor lengths smaller than a USB descriptor header. Reject too-short descriptors before using bLength to advance the local scan. This keeps the UA-101 parser robust against malformed descriptor data and matches the usual USB descriptor walking rules. Fixes: 63978ab3e3e9 ("sound: add Edirol UA-101 support") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260519-alsa-ua101-desc-len-v1-1-4307d1a5e054@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/misc/ua101.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 99e98b5e3241..586ef13097a3 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -894,8 +894,9 @@ find_format_descriptor(struct usb_interface *interface) struct uac_format_type_i_discrete_descriptor *desc; desc = (struct uac_format_type_i_discrete_descriptor *)extra; - if (desc->bLength > extralen) { - dev_err(&interface->dev, "descriptor overflow\n"); + if (desc->bLength < sizeof(struct usb_descriptor_header) || + desc->bLength > extralen) { + dev_err(&interface->dev, "invalid descriptor length\n"); return NULL; } if (desc->bLength == UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1) && From feff0251386aa6bb180a0a1cf7c1f91ba868113d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 17 May 2026 18:51:20 +0200 Subject: [PATCH 038/697] ALSA: pcm: Don't setup bogus iov_iter for silencing commit e4d3386b74fba8e01280484b67ee481ece00201e upstream. At transition to the iov_iter for PCM data transfer, we blindly applied the iov_iter setup also for silencing (i.e. data = NULL), and it leads to a calculation of bogus iov_iter. Fortunately this didn't cause troubles on most of architectures but it goes wrong on RISC-V now, causing a NULL dereference. Handle the NULL data case to treat the silencing in interleaved_copy() for addressing the bug above. noninterleaved_copy() has already the NULL data handling, so it doesn't need changes. Reported-by: Jiakai Xu Closes: https://lore.kernel.org/20260515051516.3103036-1-xujiakai24@mails.ucas.ac.cn Fixes: cf393babb37a ("ALSA: pcm: Add copy ops with iov_iter") Cc: Link: https://patch.msgid.link/20260517165121.31399-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/core/pcm_lib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 6eaa950504cf..932b9337c93e 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2138,6 +2138,9 @@ static int interleaved_copy(struct snd_pcm_substream *substream, off = frames_to_bytes(runtime, off); frames = frames_to_bytes(runtime, frames); + if (!data) + return fill_silence(substream, 0, hwoff, NULL, frames); + return do_transfer(substream, 0, hwoff, data + off, frames, transfer, in_kernel); } From 61c5017c64e2ac9e10b70b14b17a079dbc0a805f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 15 May 2026 10:55:58 +0200 Subject: [PATCH 039/697] ALSA: asihpi: Fix potential OOB array access at reading cache commit 7b7d6572145c1dab2dd9bfb550b188e5f0ff3c3f upstream. find_control() to retrieve a cached info accesses the array with the given index blindly, which may lead to an OOB array access. Add a sanity check for avoiding it. Link: https://sashiko.dev/#/patchset/20260511230121.28606-1-rosenp%40gmail.com Cc: Link: https://patch.msgid.link/20260515085606.242284-1-tiwai@suse.de Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/asihpi/hpicmn.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/pci/asihpi/hpicmn.c b/sound/pci/asihpi/hpicmn.c index 7d1abaedb46a..f06f44b13d3d 100644 --- a/sound/pci/asihpi/hpicmn.c +++ b/sound/pci/asihpi/hpicmn.c @@ -276,6 +276,12 @@ static short find_control(u16 control_index, return 0; } + if (control_index >= p_cache->control_count) { + HPI_DEBUG_LOG(VERBOSE, "control_index out of bounce %d\n", + control_index); + return 0; + } + *pI = p_cache->p_info[control_index]; if (!*pI) { HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n", From fcbd0a5fd812e9c19733de1a3c271e5b4aa8b06f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 19 May 2026 11:46:19 -0300 Subject: [PATCH 040/697] ALSA: scarlett2: Allow flash writes ending at segment boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a69b677e47a80319ce148d61cc29a2b57006e78d upstream. scarlett2_hwdep_write() rejects writes when offset + count is greater than or equal to the selected flash segment size. That incorrectly treats a write ending exactly at the end of the segment as out of space, although the last byte written is still within the segment. Split invalid argument checks from the segment-space check, keep zero-length writes as no-ops, and compare count against the remaining segment size. This permits exact-end writes and avoids relying on offset + count before deciding whether the request is in bounds. Fixes: 1abfbd3c9527 ("ALSA: scarlett2: Add support for uploading new firmware") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260519-alsa-scarlett2-flash-write-boundary-v1-1-b550480e92da@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index a75a4610663e..d0cad1d1efa3 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -9186,12 +9186,15 @@ static long scarlett2_hwdep_write(struct snd_hwdep *hw, flash_size = private->flash_segment_blocks[segment_id] * SCARLETT2_FLASH_BLOCK_SIZE; - if (count < 0 || *offset < 0 || *offset + count >= flash_size) - return -ENOSPC; + if (count < 0 || *offset < 0) + return -EINVAL; if (!count) return 0; + if (*offset >= flash_size || count > flash_size - *offset) + return -ENOSPC; + /* Limit the *req size to SCARLETT2_FLASH_RW_MAX */ if (count > max_data_size) count = max_data_size; From c32a1fbe0f9a48453a552bb315cc4f1e7a74084e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 19 May 2026 10:03:00 +0200 Subject: [PATCH 041/697] efi: Allocate runtime workqueue before ACPI init commit 13c6da02e767152c9ac4330962247a5e47011035 upstream. Since commit 5894cf571e14 ("acpi/prmt: Use EFI runtime sandbox to invoke PRM handlers") ACPI PRM calls are delegated to a workqueue which runs in a kernel thread, making it easier to detect and mitigate faulting memory accesses performed by the firmware. Rafael reports that such PRM accesses may occur before efisubsys_init() executes, which is where the workqueue is allocated, leading to NULL pointer dereferences. Since acpi_init() [which triggers the early PRM accesses] executes as a subsys_initcall() as well, and has its own dependencies that may be sensitive to initcall ordering, deferring acpi_init() is not an option. So instead, split off the workqueue allocation into its own postcore initcall, as this is the only missing piece to allow EFI runtime calls to be made. This ensures that EFI runtime call (including PRM calls) are accessible to all code running at subsys_initcall() level. Cc: Fixes: 5894cf571e14 ("acpi/prmt: Use EFI runtime sandbox to invoke PRM handlers") Reviewed-by: Rafael J. Wysocki (Intel) Signed-off-by: Ard Biesheuvel Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/efi/efi.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index c3cf5541ed68..c27fe836c8ae 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -401,21 +401,11 @@ static void __init efi_debugfs_init(void) static inline void efi_debugfs_init(void) {} #endif -/* - * We register the efi subsystem with the firmware subsystem and the - * efivars subsystem with the efi subsystem, if the system was booted with - * EFI. - */ -static int __init efisubsys_init(void) +static int __init efipostcore_init(void) { - int error; - if (!efi_enabled(EFI_RUNTIME_SERVICES)) efi.runtime_supported_mask = 0; - if (!efi_enabled(EFI_BOOT)) - return 0; - if (efi.runtime_supported_mask) { /* * Since we process only one efi_runtime_service() at a time, an @@ -427,9 +417,23 @@ static int __init efisubsys_init(void) pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); efi.runtime_supported_mask = 0; - return 0; } } + return 0; +} +postcore_initcall(efipostcore_init); + +/* + * We register the efi subsystem with the firmware subsystem and the + * efivars subsystem with the efi subsystem, if the system was booted with + * EFI. + */ +static int __init efisubsys_init(void) +{ + int error; + + if (!efi_enabled(EFI_BOOT)) + return 0; if (efi_rt_services_supported(EFI_RT_SUPPORTED_TIME_SERVICES)) platform_device_register_simple("rtc-efi", 0, NULL, 0); From 5fb947ddae55f82358967aa620679f572572f844 Mon Sep 17 00:00:00 2001 From: Krishnamoorthi M Date: Thu, 7 May 2026 23:30:51 +0530 Subject: [PATCH 042/697] spi: amd: Set correct bus number in ACPI probe path commit 422bd00b71ab42163aa3b8f8370276fe4c1581e7 upstream. On platforms where the HID2 SPI controller (AMDI0063) is enumerated via ACPI instead of PCI, amd_spi_probe() unconditionally sets bus_num to 0, while the PCI probe path assigns bus_num 2 for HID2 controller. Align the ACPI probe path to use the same bus number so that userspace and SPI client drivers see a consistent bus assignment regardless of the enumeration method. Fixes: b644c2776652 ("spi: spi_amd: Add PCI-based driver for AMD HID2 SPI controller") Cc: stable@vger.kernel.org # v6.16+ Signed-off-by: Krishnamoorthi M Link: https://patch.msgid.link/20260507180051.4158674-1-krishnamoorthi.m@amd.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-amd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 4d1dce4f4974..71a6e5c475b0 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -868,7 +868,7 @@ static int amd_spi_probe(struct platform_device *pdev) dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); amd_spi->version = (uintptr_t)device_get_match_data(dev); - host->bus_num = 0; + host->bus_num = (amd_spi->version == AMD_HID2_SPI) ? 2 : 0; return amd_spi_probe_common(dev, host); } From b737c6612c60c23b40a9f31749b99e6f61943847 Mon Sep 17 00:00:00 2001 From: Heechan Kang Date: Sun, 17 May 2026 03:47:09 +0900 Subject: [PATCH 043/697] io_uring/waitid: clear waitid info before copying it to userspace commit 93d93f5f8da791e98159795c6ef683f45bd95d13 upstream. IORING_OP_WAITID stores its result fields in struct io_waitid::info and later copies them to userspace siginfo. The prep path initializes the request arguments, but it does not initialize info itself. If the wait operation completes without reporting a child event, the common wait code can return without writing wo_info. In that case io_waitid_finish() still copies iw->info to userspace, exposing stale bytes from the reused io_kiocb command storage. Clear the result storage during prep so the io_uring path matches the regular waitid syscall, which uses a zero-initialized struct waitid_info. Fixes: f31ecf671ddc ("io_uring: add IORING_OP_WAITID support") Cc: stable@vger.kernel.org # 6.7+ Signed-off-by: Heechan Kang Link: https://patch.msgid.link/20260516184709.852814-1-gganji11@naver.com Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- io_uring/waitid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/io_uring/waitid.c b/io_uring/waitid.c index 53532ae6256c..921b4de3a31c 100644 --- a/io_uring/waitid.c +++ b/io_uring/waitid.c @@ -258,6 +258,7 @@ int io_waitid_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) iw->upid = READ_ONCE(sqe->fd); iw->options = READ_ONCE(sqe->file_index); iw->infop = u64_to_user_ptr(READ_ONCE(sqe->addr2)); + memset(&iw->info, 0, sizeof(iw->info)); return 0; } From 24840b3139d7415144b81e4f9f4c44670d15bed9 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:52:18 +0800 Subject: [PATCH 044/697] drivers/base/memory: fix memory block reference leak in poison accounting commit 03a2cc1756a0570f887d624cd6c535ea0cbd4951 upstream. memblk_nr_poison_inc() and memblk_nr_poison_sub() look up a memory block via find_memory_block_by_id(), which acquires a reference to the memory block device. Both helpers use the returned memory block without dropping that reference, leaking the device reference on each successful lookup. Drop the reference after updating nr_hwpoison. Link: https://lore.kernel.org/20260428085219.1316047-3-songmuchun@bytedance.com Fixes: 5033091de814 ("mm/hwpoison: introduce per-memory_block hwpoison counter") Signed-off-by: Muchun Song Reviewed-by: Miaohe Lin Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Cc: Danilo Krummrich Cc: Greg Kroah-Hartman Cc: "Huang, Ying" Cc: Naoya Horiguchi Cc: "Rafael J. Wysocki" Cc: Vishal Verma Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/memory.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 6d84a02cfa5d..0ecb43556736 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -1251,8 +1251,10 @@ void memblk_nr_poison_inc(unsigned long pfn) const unsigned long block_id = pfn_to_block_id(pfn); struct memory_block *mem = find_memory_block_by_id(block_id); - if (mem) + if (mem) { atomic_long_inc(&mem->nr_hwpoison); + put_device(&mem->dev); + } } void memblk_nr_poison_sub(unsigned long pfn, long i) @@ -1260,8 +1262,10 @@ void memblk_nr_poison_sub(unsigned long pfn, long i) const unsigned long block_id = pfn_to_block_id(pfn); struct memory_block *mem = find_memory_block_by_id(block_id); - if (mem) + if (mem) { atomic_long_sub(i, &mem->nr_hwpoison); + put_device(&mem->dev); + } } static unsigned long memblk_nr_poison(struct memory_block *mem) From 24de676da63c1122d2c13b0d546238b66d1b4e62 Mon Sep 17 00:00:00 2001 From: Justin Iurman Date: Wed, 20 May 2026 14:42:42 +0200 Subject: [PATCH 045/697] ipv6: ioam: refresh hdr pointer before ioam6_event() commit e46e6bc97fb1f339730ff1ba74267fbf48e7a422 upstream. Reported by Sashiko: In ipv6_hop_ioam(), the hdr pointer is initialized to point into the skb's linear data buffer. Later, the code calls skb_ensure_writable(), which might reallocate the buffer: if (skb_ensure_writable(skb, optoff + 2 + hdr->opt_len)) goto drop; /* Trace pointer may have changed */ trace = (struct ioam6_trace_hdr *)(skb_network_header(skb) + optoff + sizeof(*hdr)); ioam6_fill_trace_data(skb, ns, trace, true); ioam6_event(IOAM6_EVENT_TRACE, dev_net(skb->dev), GFP_ATOMIC, (void *)trace, hdr->opt_len - 2); If the skb is cloned or lacks sufficient linear headroom, skb_ensure_writable() will invoke pskb_expand_head(), which reallocates the skb's data buffer and frees the old one, invalidating pointers to it. While the code recalculates the trace pointer immediately after the call to skb_ensure_writable(), it fails to recalculate the hdr pointer. This patch fixes the above by recalculating the hdr pointer before passing hdr->opt_len to ioam6_event(), so that we avoid any UaF. Fixes: f655c78d6225 ("net: exthdrs: ioam6: send trace event") Cc: stable@vger.kernel.org Signed-off-by: Justin Iurman Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260520124242.32320-1-justin.iurman@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/exthdrs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index ec03bcff6b65..9b4ea568b8d7 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -955,9 +955,9 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) if (skb_ensure_writable(skb, optoff + 2 + hdr->opt_len)) goto drop; - /* Trace pointer may have changed */ - trace = (struct ioam6_trace_hdr *)(skb_network_header(skb) - + optoff + sizeof(*hdr)); + /* Trace and hdr pointers may have changed */ + hdr = (struct ioam6_hdr *)(skb_network_header(skb) + optoff); + trace = (struct ioam6_trace_hdr *)((u8 *)hdr + sizeof(*hdr)); ioam6_fill_trace_data(skb, ns, trace, true); From 2fff0cdd942261497fb8922a194b4da3315ae864 Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Fri, 1 May 2026 16:51:16 +1000 Subject: [PATCH 046/697] mm/memory: fix spurious warning when unmapping device-private/exclusive pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit be3f38d05cc5a7c3f13e51994c5dd043ab604d28 upstream. Device private and exclusive entries are only supported for anonymous folios. This condition is tested in __migrate_device_pages() and make_device_exclusive() using folio_test_anon(). However the unmap path tests this assumption using vma_is_anonymous(). This is wrong because whilst anonymous VMAs can only contain folios where folio_test_anon() is true the opposite relation does not hold. A folio for which folio_test_anon() is true does not imply vma_is_anonymous() is true. Such a condition can occur if for example a folio is part of a private filebacked mapping. In this case vma_is_anonymous() is false as the mapping is filebacked, but folio_test_anon() may be true, thus permitting devices to migrate the folio to device private memory. This can lead to the following spurious warnings during process teardown: [ 772.737706] ------------[ cut here ]------------ [ 772.739201] WARNING: mm/memory.c:1754 at unmap_page_range.cold+0x26/0x18a, CPU#17: hmm-tests/2041 [ 772.742050] Modules linked in: test_hmm nvidia_uvm(O) nvidia(O) [ 772.743959] CPU: 17 UID: 0 PID: 2041 Comm: hmm-tests Tainted: G W O 7.0.0+ #387 PREEMPT(full) [ 772.747104] Tainted: [W]=WARN, [O]=OOT_MODULE [ 772.748509] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 [ 772.752117] RIP: 0010:unmap_page_range.cold+0x26/0x18a [ 772.753780] Code: 7e fe ff ff 48 89 4c 24 78 4c 89 44 24 38 e8 f2 ff b1 00 48 8b 4c 24 78 4c 8b 44 24 38 48 8b 44 24 18 48 83 78 48 00 74 04 90 <0f> 0b 90 48 89 ca b8 ff ff 37 00 48 c1 ea 03 48 c1 e0 2a 80 3c 02 [ 772.759602] RSP: 0018:ffff888112607550 EFLAGS: 00010286 [ 772.761310] RAX: ffff88811bbf4dc0 RBX: dffffc0000000000 RCX: ffffea03e9bfffd8 [ 772.763583] RDX: 1ffff1102377e9c1 RSI: 0000000000000008 RDI: ffff88811bbf4e08 [ 772.765914] RBP: 0000000000000006 R08: ffff8881059f7448 R09: ffffed10224c0e68 [ 772.768184] R10: ffff888112607347 R11: 0000000000000001 R12: 0000000000000001 [ 772.770461] R13: ffffea03e9bfffc0 R14: ffff888112607908 R15: ffffea03e9bfffc0 [ 772.772782] FS: 00007f327caa2780(0000) GS:ffff888427b7d000(0000) knlGS:0000000000000000 [ 772.775328] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 772.777187] CR2: 00007f327ca89000 CR3: 00000001994d5000 CR4: 00000000000006f0 [ 772.779135] Call Trace: [ 772.779792] [ 772.780317] ? dmirror_interval_invalidate+0x1a3/0x290 [test_hmm] [ 772.781873] ? vm_normal_page_pud+0x2b0/0x2b0 [ 772.782992] ? __rwlock_init+0x150/0x150 [ 772.784006] ? lock_release+0x216/0x2b0 [ 772.785008] ? __mmu_notifier_invalidate_range_start+0x505/0x6e0 [ 772.786522] ? lock_release+0x216/0x2b0 [ 772.787498] ? unmap_single_vma+0xb6/0x210 [ 772.788573] unmap_vmas+0x27d/0x520 [ 772.789506] ? unmap_single_vma+0x210/0x210 [ 772.790607] ? mas_update_gap.part.0+0x620/0x620 [ 772.791834] unmap_region+0x19e/0x350 [ 772.792769] ? remove_vma+0x130/0x130 [ 772.793684] ? mas_alloc_nodes+0x1f2/0x300 [ 772.794730] vms_complete_munmap_vmas+0x8c1/0xe20 [ 772.795926] ? unmap_region+0x350/0x350 [ 772.796917] do_vmi_align_munmap+0x36a/0x4e0 [ 772.798018] ? lock_release+0x216/0x2b0 [ 772.799024] ? vma_shrink+0x620/0x620 [ 772.799983] do_vmi_munmap+0x150/0x2c0 [ 772.800939] __vm_munmap+0x161/0x2c0 [ 772.801872] ? expand_downwards+0xd60/0xd60 [ 772.802948] ? clockevents_program_event+0x1ef/0x540 [ 772.804217] ? lock_release+0x216/0x2b0 [ 772.805158] __x64_sys_munmap+0x59/0x80 [ 772.805776] do_syscall_64+0xfc/0x670 [ 772.806336] ? irqentry_exit+0xda/0x580 [ 772.806976] entry_SYSCALL_64_after_hwframe+0x4b/0x53 [ 772.807772] RIP: 0033:0x7f327cbb2717 [ 772.808323] Code: 73 01 c3 48 8b 0d f9 76 0d 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 b8 0b 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d c9 76 0d 00 f7 d8 64 89 01 48 [ 772.811337] RSP: 002b:00007ffde7f57d38 EFLAGS: 00000202 ORIG_RAX: 000000000000000b [ 772.812564] RAX: ffffffffffffffda RBX: 00007f327cc9c000 RCX: 00007f327cbb2717 [ 772.813733] RDX: 0000000000000000 RSI: 0000000000400000 RDI: 00007f327c289000 [ 772.814867] RBP: 0000000000421360 R08: 000000000000001a R09: 0000000000000000 [ 772.815991] R10: 0000000000000003 R11: 0000000000000202 R12: 00007ffde7f57d74 [ 772.817121] R13: 00007f327c689010 R14: 0000000000100000 R15: 00007f327c289000 [ 772.818272] [ 772.818614] irq event stamp: 0 [ 772.819159] hardirqs last enabled at (0): [<0000000000000000>] 0x0 [ 772.820174] hardirqs last disabled at (0): [] copy_process+0x19f3/0x6440 [ 772.821511] softirqs last enabled at (0): [] copy_process+0x1a40/0x6440 [ 772.822869] softirqs last disabled at (0): [<0000000000000000>] 0x0 [ 772.823871] ---[ end trace 0000000000000000 ]--- Fix this by using the same check for folio_test_anon() in zap_nonpresent_ptes(). Also add a hmm-test case for this. Link: https://lore.kernel.org/20260501065116.2057242-1-apopple@nvidia.com Fixes: 999dad824c39 ("mm/shmem: persist uffd-wp bit across zapping for file-backed") Signed-off-by: Alistair Popple Reported-by: Arsen Arsenović Reviewed-by: Balbir Singh Cc: David Hildenbrand Cc: Jason Gunthorpe Cc: John Hubbard Cc: Leon Romanovsky Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Peter Xu Cc: Matthew Brost Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Thomas Hellström Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 2 +- tools/testing/selftests/mm/hmm-tests.c | 50 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index 94bf107a47ca..bc6e483fc6a6 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1735,7 +1735,7 @@ static inline int zap_nonpresent_ptes(struct mmu_gather *tlb, * consider uffd-wp bit when zap. For more information, * see zap_install_uffd_wp_if_needed(). */ - WARN_ON_ONCE(!vma_is_anonymous(vma)); + WARN_ON_ONCE(!folio_test_anon(folio)); rss[mm_counter(folio)]--; folio_remove_rmap_pte(folio, page, vma); folio_put(folio); diff --git a/tools/testing/selftests/mm/hmm-tests.c b/tools/testing/selftests/mm/hmm-tests.c index 15aadaf24a66..c13b779bf118 100644 --- a/tools/testing/selftests/mm/hmm-tests.c +++ b/tools/testing/selftests/mm/hmm-tests.c @@ -998,6 +998,56 @@ TEST_F(hmm, migrate) hmm_buffer_free(buffer); } +/* + * Migrate private file memory to device private memory. + */ +TEST_F(hmm, migrate_file_private) +{ + struct hmm_buffer *buffer; + unsigned long npages; + unsigned long size; + unsigned long i; + int *ptr; + int ret; + int fd; + + npages = ALIGN(HMM_BUFFER_SIZE, self->page_size) >> self->page_shift; + ASSERT_NE(npages, 0); + size = npages << self->page_shift; + + fd = hmm_create_file(size); + ASSERT_GE(fd, 0); + + buffer = malloc(sizeof(*buffer)); + ASSERT_NE(buffer, NULL); + + buffer->fd = fd; + buffer->size = size; + buffer->mirror = malloc(size); + ASSERT_NE(buffer->mirror, NULL); + + buffer->ptr = mmap(NULL, size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + buffer->fd, 0); + ASSERT_NE(buffer->ptr, MAP_FAILED); + + /* Initialize buffer in system memory. */ + for (i = 0, ptr = buffer->ptr; i < size / sizeof(*ptr); ++i) + ptr[i] = i; + + /* Migrate memory to device. */ + ret = hmm_migrate_sys_to_dev(self->fd, buffer, npages); + ASSERT_EQ(ret, 0); + ASSERT_EQ(buffer->cpages, npages); + + /* Check what the device read. */ + for (i = 0, ptr = buffer->mirror; i < size / sizeof(*ptr); ++i) + ASSERT_EQ(ptr[i], i); + + hmm_buffer_free(buffer); +} + /* * Migrate anonymous memory to device private memory and fault some of it back * to system memory, then try migrating the resulting mix of system and device From 62153767e8fc3889bc6508e9ffe927aaf64c4334 Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Thu, 30 Apr 2026 13:31:22 +0200 Subject: [PATCH 047/697] mm: fix __vm_normal_page() to handle missing support for pmd_special()/pud_special() commit c0c6ccd9828c3a1950623b546fa57292a77b5c73 upstream. On x86 32-bit with THP enabled, zap_huge_pmd() is seen to generate a "WARNING: mm/memory.c:735 at __vm_normal_page+0x6a/0x7d", from the VM_WARN_ON_ONCE(is_zero_pfn(pfn) || is_huge_zero_pfn(pfn)); followed by "BUG: Bad rss-counter state"s, then later "BUG: Bad page state"s when reclaim gets to call shrink_huge_zero_folio_scan(). It's as if the _PAGE_SPECIAL bit never got set in the huge_zero pmd: and indeed, whereas pte_special() and pte_mkspecial() are subject to a dedicated CONFIG_ARCH_HAS_PTE_SPECIAL, pmd_special() and pmd_mkspecial() are subject to CONFIG_ARCH_SUPPORTS_PMD_PFNMAP, which is never enabled on any 32-bit architecture. While the problem was exposed through commit d80a9cb1a64a ("mm/huge_memory: add and use normal_or_softleaf_folio_pmd()"), it was an oversight in commit af38538801c6 ("mm/memory: factor out common code from vm_normal_page_*()") and would result in other problems: * huge zero folio accounted in smaps, pagemap (PAGE_IS_FILE) and numamaps as file-backed THP * folio_walk_start() returning the folio even without FW_ZEROPAGE set. Callers seem to tolerate that, though. ... and triggering the VM_WARN_ON_ONE(), although never reported so far. To fix it, teach vm_normal_page_pmd()/vm_normal_page_pud() to consider whether pmd_special/pud_special is actually implemented. Link: https://lore.kernel.org/20260430-pmd_special-v1-1-dbcbcfd72c20@kernel.org Fixes: af38538801c6 ("mm/memory: factor out common code from vm_normal_page_*()") Signed-off-by: David Hildenbrand (Arm) Reported-by: Hugh Dickins Closes: https://lore.kernel.org/r/74a75b59-2e13-3985-ee99-d5521f39df2a@google.com Reported-by: Bibo Mao Closes: https://lore.kernel.org/r/20260430041121.2839350-1-maobibo@loongson.cn Debugged-by: Hugh Dickins Reviewed-by: Lance Yang Tested-by: Bibo Mao Reviewed-by: Baolin Wang Reviewed-by: Oscar Salvador Reviewed-by: Lorenzo Stoakes Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index bc6e483fc6a6..42b733b78523 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -612,6 +612,21 @@ static void print_bad_page_map(struct vm_area_struct *vma, dump_stack(); add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); } + +static inline bool pgtable_level_has_pxx_special(enum pgtable_level level) +{ + switch (level) { + case PGTABLE_LEVEL_PTE: + return IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL); + case PGTABLE_LEVEL_PMD: + return IS_ENABLED(CONFIG_ARCH_SUPPORTS_PMD_PFNMAP); + case PGTABLE_LEVEL_PUD: + return IS_ENABLED(CONFIG_ARCH_SUPPORTS_PUD_PFNMAP); + default: + return false; + } +} + #define print_bad_pte(vma, addr, pte, page) \ print_bad_page_map(vma, addr, pte_val(pte), page, PGTABLE_LEVEL_PTE) @@ -684,7 +699,7 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, bool special, unsigned long long entry, enum pgtable_level level) { - if (IS_ENABLED(CONFIG_ARCH_HAS_PTE_SPECIAL)) { + if (pgtable_level_has_pxx_special(level)) { if (unlikely(special)) { #ifdef CONFIG_FIND_NORMAL_PAGE if (vma->vm_ops && vma->vm_ops->find_normal_page) @@ -699,8 +714,9 @@ static inline struct page *__vm_normal_page(struct vm_area_struct *vma, return NULL; } /* - * With CONFIG_ARCH_HAS_PTE_SPECIAL, any special page table - * mappings (incl. shared zero folios) are marked accordingly. + * With working pte_special()/pmd_special()..., any special page + * table mappings (incl. shared zero folios) are marked + * accordingly. */ } else { if (unlikely(vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP))) { From 09ce923071e7852ece60d7368e05249bf32c7967 Mon Sep 17 00:00:00 2001 From: Muchun Song Date: Tue, 28 Apr 2026 16:52:17 +0800 Subject: [PATCH 048/697] mm/memory_hotplug: fix memory block reference leak on remove commit 93866f55f7e292fe3d47d36c9efe5ee10213a06b upstream. Patch series "mm: Fix memory block leaks and locking", v2. This series fixes two memory block device reference leaks and one locking issue around the per-memory_block hwpoison counter. This patch (of 2): remove_memory_blocks_and_altmaps() looks up each memory block with find_memory_block(), which acquires a reference to the memory block device. That reference is never dropped on this path, resulting in a leaked device reference when removing memory blocks and their altmaps. Drop the reference after retrieving mem->altmap and clearing mem->altmap, before removing the memory block device. Link: https://lore.kernel.org/20260428085219.1316047-1-songmuchun@bytedance.com Link: https://lore.kernel.org/20260428085219.1316047-2-songmuchun@bytedance.com Fixes: 6b8f0798b85a ("mm/memory_hotplug: split memmap_on_memory requests across memblocks") Signed-off-by: Muchun Song Acked-by: Oscar Salvador Acked-by: David Hildenbrand (Arm) Cc: Danilo Krummrich Cc: Greg Kroah-Hartman Cc: "Huang, Ying" Cc: Miaohe Lin Cc: Naoya Horiguchi Cc: "Rafael J. Wysocki" Cc: Vishal Verma Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memory_hotplug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index aa1f74414307..6a7714179c20 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1439,6 +1439,8 @@ static void remove_memory_blocks_and_altmaps(u64 start, u64 size) altmap = mem->altmap; mem->altmap = NULL; + /* drop the ref. we got via find_memory_block() */ + put_device(&mem->dev); remove_memory_block_devices(cur_start, memblock_size); From 738d18f1da3513d17b6f7bf30146cc4ac2480ffd Mon Sep 17 00:00:00 2001 From: "David Hildenbrand (Arm)" Date: Tue, 21 Apr 2026 17:39:07 +0200 Subject: [PATCH 049/697] mm/page_alloc: fix initialization of tags of the huge zero folio with init_on_free commit 6a288a4ddb4a994490505ab5f41c445f8e6b6467 upstream. __GFP_ZEROTAGS semantics are currently a bit weird, but effectively this flag is only ever set alongside __GFP_ZERO and __GFP_SKIP_KASAN. If we run with init_on_free, we will zero out pages during __free_pages_prepare(), to skip zeroing on the allocation path. However, when allocating with __GFP_ZEROTAG set, post_alloc_hook() will consequently not only skip clearing page content, but also skip clearing tag memory. Not clearing tags through __GFP_ZEROTAGS is irrelevant for most pages that will get mapped to user space through set_pte_at() later: set_pte_at() and friends will detect that the tags have not been initialized yet (PG_mte_tagged not set), and initialize them. However, for the huge zero folio, which will be mapped through a PMD marked as special, this initialization will not be performed, ending up exposing whatever tags were still set for the pages. The docs (Documentation/arch/arm64/memory-tagging-extension.rst) state that allocation tags are set to 0 when a page is first mapped to user space. That no longer holds with the huge zero folio when init_on_free is enabled. Fix it by decoupling __GFP_ZEROTAGS from __GFP_ZERO, passing to tag_clear_highpages() whether we want to also clear page content. Invert the meaning of the tag_clear_highpages() return value to have clearer semantics. Reproduced with the huge zero folio by modifying the check_buffer_fill arm64/mte selftest to use a 2 MiB area, after making sure that pages have a non-0 tag set when freeing (note that, during boot, we will not actually initialize tags, but only set KASAN_TAG_KERNEL in the page flags). $ ./check_buffer_fill 1..20 ... not ok 17 Check initial tags with private mapping, sync error mode and mmap memory not ok 18 Check initial tags with private mapping, sync error mode and mmap/mprotect memory ... This code needs more cleanups; we'll tackle that next, like decoupling __GFP_ZEROTAGS from __GFP_SKIP_KASAN. [akpm@linux-foundation.org: s/__GPF_ZERO/__GFP_ZERO/, per David] Link: https://lore.kernel.org/20260421-zerotags-v2-1-05cb1035482e@kernel.org Fixes: adfb6609c680 ("mm/huge_memory: initialise the tags of the huge zero folio") Signed-off-by: David Hildenbrand (Arm) Reviewed-by: Catalin Marinas Tested-by: Lance Yang Cc: Brendan Jackman Cc: Dev Jain Cc: Johannes Weiner Cc: Liam Howlett Cc: Lorenzo Stoakes (Oracle) Cc: Mark Brown Cc: Michal Hocko Cc: Mike Rapoport Cc: Ryan Roberts Cc: Suren Baghdasaryan Cc: Will Deacon Cc: Zi Yan Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/page.h | 2 +- arch/arm64/mm/fault.c | 11 +++++++---- include/linux/gfp_types.h | 10 +++++----- include/linux/highmem.h | 7 ++++--- mm/page_alloc.c | 8 ++++---- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h index 258cca4b4873..5062d8725fda 100644 --- a/arch/arm64/include/asm/page.h +++ b/arch/arm64/include/asm/page.h @@ -33,7 +33,7 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma, unsigned long vaddr); #define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio -bool tag_clear_highpages(struct page *to, int numpages); +bool tag_clear_highpages(struct page *to, int numpages, bool clear_pages); #define __HAVE_ARCH_TAG_CLEAR_HIGHPAGES #define clear_user_page(page, vaddr, pg) clear_page(page) diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index a193b6a5d1e6..4c62082b9a3b 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -967,7 +967,7 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma, return vma_alloc_folio(flags, 0, vma, vaddr); } -bool tag_clear_highpages(struct page *page, int numpages) +bool tag_clear_highpages(struct page *page, int numpages, bool clear_pages) { /* * Check if MTE is supported and fall back to clear_highpage(). @@ -975,13 +975,16 @@ bool tag_clear_highpages(struct page *page, int numpages) * post_alloc_hook() will invoke tag_clear_highpages(). */ if (!system_supports_mte()) - return false; + return clear_pages; /* Newly allocated pages, shouldn't have been tagged yet */ for (int i = 0; i < numpages; i++, page++) { WARN_ON_ONCE(!try_page_mte_tagging(page)); - mte_zero_clear_page_tags(page_address(page)); + if (clear_pages) + mte_zero_clear_page_tags(page_address(page)); + else + mte_clear_page_tags(page_address(page)); set_page_mte_tagged(page); } - return true; + return false; } diff --git a/include/linux/gfp_types.h b/include/linux/gfp_types.h index 65db9349f905..63bee08c6f31 100644 --- a/include/linux/gfp_types.h +++ b/include/linux/gfp_types.h @@ -277,11 +277,11 @@ enum { * * %__GFP_ZERO returns a zeroed page on success. * - * %__GFP_ZEROTAGS zeroes memory tags at allocation time if the memory itself - * is being zeroed (either via __GFP_ZERO or via init_on_alloc, provided that - * __GFP_SKIP_ZERO is not set). This flag is intended for optimization: setting - * memory tags at the same time as zeroing memory has minimal additional - * performance impact. + * %__GFP_ZEROTAGS zeroes memory tags at allocation time. Setting memory tags at + * the same time as zeroing memory (e.g., with __GFP_ZERO) has minimal + * additional performance impact. However, __GFP_ZEROTAGS also zeroes the tags + * even if memory is not getting zeroed at allocation time (e.g., + * with init_on_free). * * %__GFP_SKIP_KASAN makes KASAN skip unpoisoning on page allocation. * Used for userspace and vmalloc pages; the latter are unpoisoned by diff --git a/include/linux/highmem.h b/include/linux/highmem.h index abc20f9810fd..029172383b76 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -251,10 +251,11 @@ static inline void clear_highpage_kasan_tagged(struct page *page) #ifndef __HAVE_ARCH_TAG_CLEAR_HIGHPAGES -/* Return false to let people know we did not initialize the pages */ -static inline bool tag_clear_highpages(struct page *page, int numpages) +/* Returns true if the caller has to initialize the pages */ +static inline bool tag_clear_highpages(struct page *page, int numpages, + bool clear_pages) { - return false; + return clear_pages; } #endif diff --git a/mm/page_alloc.c b/mm/page_alloc.c index f67696618003..775e02b797a3 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1847,9 +1847,9 @@ static inline bool should_skip_init(gfp_t flags) inline void post_alloc_hook(struct page *page, unsigned int order, gfp_t gfp_flags) { + const bool zero_tags = gfp_flags & __GFP_ZEROTAGS; bool init = !want_init_on_free() && want_init_on_alloc(gfp_flags) && !should_skip_init(gfp_flags); - bool zero_tags = init && (gfp_flags & __GFP_ZEROTAGS); int i; set_page_private(page, 0); @@ -1871,11 +1871,11 @@ inline void post_alloc_hook(struct page *page, unsigned int order, */ /* - * If memory tags should be zeroed - * (which happens only when memory should be initialized as well). + * Clearing tags can efficiently clear the memory for us as well, if + * required. */ if (zero_tags) - init = !tag_clear_highpages(page, 1 << order); + init = tag_clear_highpages(page, 1 << order, /* clear_pages= */init); if (!should_skip_kasan_unpoison(gfp_flags) && kasan_unpoison_pages(page, order, init)) { From 0fa24311bd42df98296ec2ae94d602e55ff0eb2a Mon Sep 17 00:00:00 2001 From: Luiz Capitulino Date: Mon, 27 Apr 2026 12:03:51 -0400 Subject: [PATCH 050/697] selftests/mm: run_vmtests.sh: fix destructive tests invocation commit 3432cbb291aabf85f8af4b9d1ec37179168ff999 upstream. Destructive tests should be invoked with -d command-line option, but this won't work today since 'd' is missing in getopts command-line. This commit fixes it. Link: https://lore.kernel.org/214fd9e4-5398-4c26-859e-c982c2e277c3@redhat.com Fixes: f16ff3b692ad ("selftests/mm: run_vmtests.sh: add missing tests") Signed-off-by: Luiz Capitulino Reviewed-by: Mike Rapoport (Microsoft) Reviewed-by: SeongJae Park Cc: David Hildenbrand Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/mm/run_vmtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh index d9173f2312b7..1edb2d785d93 100755 --- a/tools/testing/selftests/mm/run_vmtests.sh +++ b/tools/testing/selftests/mm/run_vmtests.sh @@ -97,7 +97,7 @@ RUN_ALL=false RUN_DESTRUCTIVE=false TAP_PREFIX="# " -while getopts "aht:n" OPT; do +while getopts "aht:nd" OPT; do case ${OPT} in "a") RUN_ALL=true ;; "h") usage ;; From ffb6dbb49c96be82f07c7b112e3ebc3e6fdd8dd5 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Tue, 19 May 2026 11:57:39 +0530 Subject: [PATCH 051/697] net: wwan: iosm: fix potential memory leaks in ipc_imem_init() commit c5d93b2c40355e999715262a824965aac025a427 upstream. The memory allocated in ipc_protocol_init() is not freed on the error paths that follow in ipc_imem_init(). Fix that by calling the corresponding release function ipc_protocol_deinit() in the error path. Fixes: 3670970dd8c6 ("net: iosm: shared memory IPC interface") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Link: https://patch.msgid.link/20260519062815.55545-1-nihaal@cse.iitm.ac.in Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wwan/iosm/iosm_ipc_imem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wwan/iosm/iosm_ipc_imem.c b/drivers/net/wwan/iosm/iosm_ipc_imem.c index 530a3ea47a1a..ce7604500486 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_imem.c +++ b/drivers/net/wwan/iosm/iosm_ipc_imem.c @@ -1426,6 +1426,8 @@ struct iosm_imem *ipc_imem_init(struct iosm_pcie *pcie, unsigned int device_id, protocol_init_fail: cancel_work_sync(&ipc_imem->run_state_worker); ipc_task_deinit(ipc_imem->ipc_task); + if (ipc_imem->ipc_protocol) + ipc_protocol_deinit(ipc_imem->ipc_protocol); ipc_task_init_fail: kfree(ipc_imem->ipc_task); ipc_task_fail: From added1213395071470a900cc845a042fb51882a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Safa=20Karaku=C5=9F?= Date: Sat, 16 May 2026 21:15:04 +0300 Subject: [PATCH 052/697] Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ab1513597c6cf17cd1ad2a21e3b045421b48e022 upstream. bt_accept_dequeue() unlinks a not-yet-accepted child from the parent accept queue and release_sock()s it before returning, so the returned sk has no caller reference and is unlocked. l2cap_sock_cleanup_listen() walks these children on listening-socket close. A concurrent HCI disconnect drives hci_rx_work -> l2cap_conn_del() which runs l2cap_chan_del() + l2cap_sock_kill() and frees the child sk and its l2cap_chan; cleanup_listen() then uses both: BUG: KASAN: slab-use-after-free in l2cap_sock_kill l2cap_sock_kill / l2cap_sock_cleanup_listen / __x64_sys_close Freed by: l2cap_conn_del -> l2cap_sock_close_cb -> l2cap_sock_kill This is distinct from the two fixes already in this area: commit e83f5e24da741 ("Bluetooth: serialize accept_q access") serialises the accept_q list/poll and takes temporary refs inside bt_accept_dequeue(), and CVE-2025-39860 serialises the userspace close()/accept() race by calling cleanup_listen() under lock_sock() in l2cap_sock_release(). Neither covers l2cap_conn_del() running from hci_rx_work, so this UAF still reproduces on current bluetooth/master. Take the reference at the source: bt_accept_dequeue() does sock_hold() while sk is still locked, before release_sock(); callers sock_put(). cleanup_listen() pins the chan with l2cap_chan_hold_unless_zero() under a brief child sk lock (serialising vs l2cap_sock_teardown_cb()), drops it before l2cap_chan_lock(), and skips a duplicate l2cap_sock_kill() on SOCK_DEAD. conn->lock is not taken here: cleanup_listen() runs under the parent sk lock and that would invert conn->lock -> chan->lock -> sk_lock (lockdep). KASAN/SMP: an unprivileged listen/close vs HCI-disconnect race produced 12 use-after-free reports per run before this change; 0, and no lockdep report, over 1600+ raced iterations after it on bluetooth/master. Fixes: 15f02b910562 ("Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode") Cc: stable@vger.kernel.org Reported-by: Siwei Zhang Reviewed-by: Siwei Zhang Signed-off-by: Safa Karakuş Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/af_bluetooth.c | 10 +++++++ net/bluetooth/iso.c | 9 ++++++- net/bluetooth/l2cap_sock.c | 51 +++++++++++++++++++++++++++++++----- net/bluetooth/rfcomm/sock.c | 9 ++++++- net/bluetooth/sco.c | 9 ++++++- 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 2b94e2077203..bcdf086b8ca5 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -309,6 +309,16 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) if (newsock) sock_graft(sk, newsock); + /* Hand the caller a reference taken while sk is + * still locked. bt_accept_unlink() just dropped + * the accept-queue reference; without this hold a + * concurrent teardown (e.g. l2cap_conn_del() -> + * l2cap_sock_kill()) could free sk between + * release_sock() and the caller using it. Every + * caller drops this with sock_put() when done. + */ + sock_hold(sk); + release_sock(sk); return sk; } diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 46ebd69026fe..3b0385071bb4 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -741,6 +741,8 @@ static void iso_sock_cleanup_listen(struct sock *parent) while ((sk = bt_accept_dequeue(parent, NULL))) { iso_sock_close(sk); iso_sock_kill(sk); + /* Drop the reference handed back by bt_accept_dequeue(). */ + sock_put(sk); } /* If listening socket has a hcon, properly disconnect it */ @@ -1282,8 +1284,13 @@ static int iso_sock_accept(struct socket *sock, struct socket *newsock, } ch = bt_accept_dequeue(sk, newsock); - if (ch) + if (ch) { + /* Drop the bridging ref from bt_accept_dequeue(); + * the grafted socket keeps ch alive from here. + */ + sock_put(ch); break; + } if (!timeo) { err = -EAGAIN; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 15637402a39d..898ee21d7e4f 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -349,8 +349,13 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, } nsk = bt_accept_dequeue(sk, newsock); - if (nsk) + if (nsk) { + /* Drop the bridging ref from bt_accept_dequeue(); + * the grafted socket keeps nsk alive from here. + */ + sock_put(nsk); break; + } if (!timeo) { err = -EAGAIN; @@ -1457,22 +1462,54 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) BT_DBG("parent %p state %s", parent, state_to_string(parent->sk_state)); - /* Close not yet accepted channels */ + /* Close not yet accepted channels. + * + * bt_accept_dequeue() now returns sk with an extra reference held + * (taken while sk was still locked) so a concurrent l2cap_conn_del() + * -> l2cap_sock_kill() cannot free sk under us. + * + * cleanup_listen() runs under the parent sk lock, so unlike + * l2cap_sock_shutdown() we must NOT take conn->lock here: that would + * establish sk_lock -> conn->lock and invert the established + * conn->lock -> chan->lock -> sk_lock order (lockdep deadlock). + * + * Instead, briefly take the child sk lock to fetch and pin its chan. + * l2cap_conn_del() reaches the chan free only via + * l2cap_chan_del() -> l2cap_sock_teardown_cb(), which itself takes + * the child sk lock; holding it across l2cap_chan_hold_unless_zero() + * therefore guarantees the chan cannot be freed while we read and + * pin it (hold_unless_zero() additionally skips a chan already past + * its last reference). We then drop the sk lock before taking + * chan->lock, so sk and chan locks are never held together. + */ while ((sk = bt_accept_dequeue(parent, NULL))) { - struct l2cap_chan *chan = l2cap_pi(sk)->chan; + struct l2cap_chan *chan; + + lock_sock_nested(sk, L2CAP_NESTING_NORMAL); + chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan); + release_sock(sk); + if (!chan) { + /* l2cap_conn_del() already tearing this child down */ + sock_put(sk); + continue; + } BT_DBG("child chan %p state %s", chan, state_to_string(chan->state)); - l2cap_chan_hold(chan); l2cap_chan_lock(chan); - __clear_chan_timer(chan); l2cap_chan_close(chan, ECONNRESET); - l2cap_sock_kill(sk); - + /* l2cap_conn_del() may already have killed this socket + * (it sets SOCK_DEAD); skip the duplicate to avoid a + * double sock_put()/l2cap_chan_put(). + */ + if (!sock_flag(sk, SOCK_DEAD)) + l2cap_sock_kill(sk); l2cap_chan_unlock(chan); + l2cap_chan_put(chan); + sock_put(sk); } } diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 913402806fa0..3052436e9c6d 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -180,6 +180,8 @@ static void rfcomm_sock_cleanup_listen(struct sock *parent) while ((sk = bt_accept_dequeue(parent, NULL))) { rfcomm_sock_close(sk); rfcomm_sock_kill(sk); + /* Drop the reference handed back by bt_accept_dequeue(). */ + sock_put(sk); } parent->sk_state = BT_CLOSED; @@ -496,8 +498,13 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, } nsk = bt_accept_dequeue(sk, newsock); - if (nsk) + if (nsk) { + /* Drop the bridging ref from bt_accept_dequeue(); + * the grafted socket keeps nsk alive from here. + */ + sock_put(nsk); break; + } if (!timeo) { err = -EAGAIN; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 9404fdb10ea6..a536c2edd14f 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -498,6 +498,8 @@ static void sco_sock_cleanup_listen(struct sock *parent) while ((sk = bt_accept_dequeue(parent, NULL))) { sco_sock_close(sk); sco_sock_kill(sk); + /* Drop the reference handed back by bt_accept_dequeue(). */ + sock_put(sk); } parent->sk_state = BT_CLOSED; @@ -759,8 +761,13 @@ static int sco_sock_accept(struct socket *sock, struct socket *newsock, } ch = bt_accept_dequeue(sk, newsock); - if (ch) + if (ch) { + /* Drop the bridging ref from bt_accept_dequeue(); + * the grafted socket keeps ch alive from here. + */ + sock_put(ch); break; + } if (!timeo) { err = -EAGAIN; From 61f2410a96dee808029e2ae4d6ef2dd635f3477f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 15 May 2026 07:25:25 +0100 Subject: [PATCH 053/697] Bluetooth: ISO: drop ISO_END frames received without prior ISO_START commit 84c24fb151fc1179355296d7ff29129ac7c42129 upstream. ISO data PDUs carry a packet-boundary flag indicating START, CONT, END or SINGLE. The ISO_CONT branch of iso_recv() guards against a missing ISO_START by checking conn->rx_len before touching conn->rx_skb, but ISO_END does not. If a peer sends an ISO_END as the first packet on a fresh ISO connection, conn->rx_skb is still NULL and conn->rx_len is zero, so skb_put(conn->rx_skb, ...) dereferences NULL and oopses. For BIS, where receivers sync to a broadcaster without pairing, any broadcaster on the air can trigger this. Mirror the ISO_CONT check at the top of ISO_END so a stray end fragment is logged and dropped instead of crashing the host. Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: David Carlier Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/iso.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 3b0385071bb4..038e292fb194 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -2461,6 +2461,11 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags) break; case ISO_END: + if (!conn->rx_len) { + BT_ERR("Unexpected end frame (len %d)", skb->len); + goto drop; + } + skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), skb->len); conn->rx_len -= skb->len; From 5506aec795135cdd4cbf4e845929155663b25055 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Tue, 12 May 2026 22:15:39 +0200 Subject: [PATCH 054/697] Bluetooth: bnep: Fix UAF read of dev->name commit 59e932ded949fa6f0340bf7c6d7818f962fa4fd2 upstream. bnep_add_connection() needs to keep holding the bnep_session_sem while reading dev->name (just like bnep_get_connlist() does); otherwise the bnep_session() thread can concurrently free the net_device, which can for example be triggered by a concurrent bnep_del_connection(). (This UAF is fairly uninteresting from a security perspective; calling bnep_add_connection() requires passing a capable(CAP_NET_ADMIN) check. It also requires completely tearing down a netdev during a fairly tight race window.) Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jann Horn Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/bnep/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d44987d4515c..b3cef7a4db54 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -638,8 +638,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) goto failed; } - up_write(&bnep_session_sem); strcpy(req->device, dev->name); + up_write(&bnep_session_sem); return 0; failed: From 192cb0f1ca706d9a1bc36ae0ad5f666d1e4fd894 Mon Sep 17 00:00:00 2001 From: Mingyu Wang <25181214217@stu.xidian.edu.cn> Date: Mon, 18 May 2026 10:49:49 +0800 Subject: [PATCH 055/697] Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths commit c1bb9336ae6b54a5f6a353c4bd4ed9a4307e429b upstream. Vulnerabilities leading to Use-After-Free (UAF) and Null Pointer Dereference (NPD) conditions were observed in the lifecycle management of hci_uart. The primary issue arises because the workqueues (init_ready and write_work) are only flushed/cancelled if the HCI_UART_PROTO_READY flag is set during TTY close. If a hangup occurs before setup completes, hci_uart_tty_close() skips the teardown of these workqueues and proceeds to free the `hu` struct. When the scheduled work executes later, it blindly dereferences the freed `hu` struct. Furthermore, several data races and UAFs were identified in the teardown sequence: 1. Calling hci_uart_flush() from hci_uart_close() without effectively disabling write_work causes a race condition where both can concurrently double-free hu->tx_skb. This happens because protocol timers can concurrently invoke hci_uart_tx_wakeup() and requeue write_work. 2. Calling hci_free_dev(hdev) before hu->proto->close(hu) causes a UAF when vendor specific protocol close callbacks dereference hu->hdev. 3. In the initialization error paths, failing to take the proto_lock write lock before clearing PROTO_READY leads to races with active readers. Additionally, hci_uart_tty_receive() accesses hu->hdev outside the read lock, leading to UAFs if the initialization error path frees hdev concurrently. Fix these synchronization and lifecycle issues by: 1. Re-ordering hci_uart_tty_close() to clear HCI_UART_PROTO_READY first, followed immediately by a cancel_work_sync(&hu->write_work). Clearing the flag locks out concurrent protocol timers from successfully invoking hci_uart_tx_wakeup(), effectively rendering the cancellation permanent and preventing the tx_skb double-free. 2. Note: Clearing PROTO_READY early causes hci_uart_close() to skip hu->proto->flush(). This is perfectly safe in the tty_close path because hu->proto->close() executes shortly after, which intrinsically purges all protocol SKB queues and tears down the state. 3. Relocating hu->proto->close(hu) strictly prior to hci_free_dev(hdev) across all close and error paths to prevent vendor-level UAFs. 4. Moving the hdev->stat.byte_rx increment in hci_uart_tty_receive() inside the proto_lock read-side critical section to safely synchronize with device unregistration. 5. Adding cancel_work_sync(&hu->write_work) to hci_uart_close() to safely flush the workqueue before hci_uart_flush() is invoked via the HCI core. 6. Utilizing cancel_work_sync() instead of disable_work_sync() across all paths to prevent permanently breaking user-space retry capabilities. Fixes: 3b799254cf6f ("Bluetooth: hci_uart: Cancel init work before unregistering") Cc: stable@vger.kernel.org Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn> Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_ldisc.c | 48 +++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 5455990ab211..10e936b87cc8 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -194,7 +194,15 @@ void hci_uart_init_work(struct work_struct *work) err = hci_register_dev(hu->hdev); if (err < 0) { BT_ERR("Can't register HCI device"); + + percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); + percpu_up_write(&hu->proto_lock); + + /* Safely cancel work after clearing flags */ + cancel_work_sync(&hu->write_work); + + /* Close protocol before freeing hdev */ hu->proto->close(hu); hdev = hu->hdev; hu->hdev = NULL; @@ -263,8 +271,12 @@ static int hci_uart_open(struct hci_dev *hdev) /* Close device */ static int hci_uart_close(struct hci_dev *hdev) { + struct hci_uart *hu = hci_get_drvdata(hdev); + BT_DBG("hdev %p", hdev); + cancel_work_sync(&hu->write_work); + hci_uart_flush(hdev); hdev->flush = NULL; return 0; @@ -531,6 +543,7 @@ static void hci_uart_tty_close(struct tty_struct *tty) { struct hci_uart *hu = tty->disc_data; struct hci_dev *hdev; + bool proto_ready; BT_DBG("tty %p", tty); @@ -540,24 +553,38 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (!hu) return; - hdev = hu->hdev; - if (hdev) - hci_uart_close(hdev); + /* Wait for init_ready to finish to prevent registration races */ + cancel_work_sync(&hu->init_ready); - if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) { + proto_ready = test_bit(HCI_UART_PROTO_READY, &hu->flags); + if (proto_ready) { percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_READY, &hu->flags); percpu_up_write(&hu->proto_lock); + } - cancel_work_sync(&hu->init_ready); - cancel_work_sync(&hu->write_work); + /* + * Unconditionally cancel write_work AFTER clearing PROTO_READY. + * This ensures that concurrent protocol timers cannot requeue + * write_work via hci_uart_tx_wakeup(), permanently preventing + * double-free races and UAFs. + */ + cancel_work_sync(&hu->write_work); + + hdev = hu->hdev; + if (hdev) + hci_uart_close(hdev); /* proto->flush is safely skipped */ + if (proto_ready) { if (hdev) { if (test_bit(HCI_UART_REGISTERED, &hu->flags)) hci_unregister_dev(hdev); - hci_free_dev(hdev); } + /* Close protocol before freeing hdev (intrinsically purges queues) */ hu->proto->close(hu); + + if (hdev) + hci_free_dev(hdev); } clear_bit(HCI_UART_PROTO_SET, &hu->flags); @@ -625,11 +652,12 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, * tty caller */ hu->proto->recv(hu, data, count); - percpu_up_read(&hu->proto_lock); if (hu->hdev) hu->hdev->stat.byte_rx += count; + percpu_up_read(&hu->proto_lock); + tty_unthrottle(tty); } @@ -695,6 +723,10 @@ static int hci_uart_register_dev(struct hci_uart *hu) percpu_down_write(&hu->proto_lock); clear_bit(HCI_UART_PROTO_INIT, &hu->flags); percpu_up_write(&hu->proto_lock); + /* Cancel work after clearing flags */ + cancel_work_sync(&hu->write_work); + + /* Close protocol before freeing hdev */ hu->proto->close(hu); hu->hdev = NULL; hci_free_dev(hdev); From 051922ab709c0a6917eae765c22481dfc68379e5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 11 May 2026 08:26:41 -0400 Subject: [PATCH 056/697] Bluetooth: L2CAP: ecred_reconfigure: send packed pdu, not stack pointer commit 3374ef8cf99368a40f7efd51a2a375a4c5dc6f0d upstream. Commit 1c08108f3014 ("Bluetooth: L2CAP: Avoid -Wflex-array-member-not-at-end warnings") converted the on-stack request PDU in l2cap_ecred_reconfigure() from an explicit packed struct to DEFINE_RAW_FLEX(), but did not adjust the size and source-pointer arguments to l2cap_send_cmd(): - struct { - struct l2cap_ecred_reconf_req req; - __le16 scid; - } pdu; + DEFINE_RAW_FLEX(struct l2cap_ecred_reconf_req, pdu, scid, 1); ... l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ, sizeof(pdu), &pdu); After the conversion, DEFINE_RAW_FLEX() expands to declare an anonymous union pdu_u plus a local pointer "pdu" pointing at it. Therefore: - sizeof(pdu) is now sizeof(struct l2cap_ecred_reconf_req *) = 8 on 64-bit (4 on 32-bit), not the 6 bytes of (mtu, mps, scid[1]). - &pdu is the address of the local pointer's stack storage, not the address of the request payload. l2cap_send_cmd() forwards (data, count) to l2cap_build_cmd(), which calls skb_put_data(skb, data, count). The L2CAP_ECRED_RECONFIGURE_REQ packet body therefore contains 8 bytes copied from the kernel stack starting at &pdu -- the 8 bytes overlap the pdu pointer's value, leaking a kernel stack address to the paired Bluetooth peer. The intended (mtu, mps, scid) fields are not transmitted at all, so the peer rejects the request as malformed and the L2CAP_ECRED_RECONFIGURE feature itself has been broken for the local-side initiator since the introducing commit landed. The sibling site l2cap_ecred_conn_req() in the same commit was converted correctly (sizeof(*pdu) + len, pdu); only this site was missed. Restore the original semantics: pass the full flex-struct size via struct_size(pdu, scid, 1) and the pdu pointer (the struct address) as the source. Validated on a stock 7.0-based host kernel via the real call path: setsockopt(SOL_BLUETOOTH, BT_RCVMTU, ...) on a BT_CONNECTED L2CAP_MODE_EXT_FLOWCTL socket emits an L2CAP_ECRED_RECONFIGURE_REQ whose body is 8 bytes (the on-stack pdu local's value) rather than the expected 6. Three captures from fresh socket / fresh hciemu peer on the same host -- low bytes vary per call, high 0xffff confirms a kernel virtual address (KASLR-randomised stack slot, not a fixed string): RECONF_REQ body (ident=0x02 len=8): 42 fb 54 af 0e ca ff ff RECONF_REQ body (ident=0x02 len=8): 52 3d 2e af 0e ca ff ff RECONF_REQ body (ident=0x02 len=8): b2 fc 5b af 0e ca ff ff After this patch the body is 6 bytes carrying the expected little-endian (mtu, mps, scid). Cc: stable@vger.kernel.org Fixes: 1c08108f3014 ("Bluetooth: L2CAP: Avoid -Wflex-array-member-not-at-end warnings") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bcb13ce53109..87ebe81277c5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7275,7 +7275,7 @@ static void l2cap_ecred_reconfigure(struct l2cap_chan *chan) chan->ident = l2cap_get_ident(conn); l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ, - sizeof(pdu), &pdu); + struct_size(pdu, scid, 1), pdu); } int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu) From f1febe93ef075314615f970a87681d9ab86691d1 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 10:38:19 -0400 Subject: [PATCH 057/697] Bluetooth: MGMT: validate Add Extended Advertising Data length commit d3f7d17960ed50df3a6709c5158caff989c8c905 upstream. MGMT_OP_ADD_EXT_ADV_DATA is registered as a variable-length command, with MGMT_ADD_EXT_ADV_DATA_SIZE as the fixed header size. The handler then uses cp->adv_data_len and cp->scan_rsp_len to validate and copy cp->data, but it never checks that those bytes are part of the mgmt command payload. A short command can therefore make add_ext_adv_data() pass an out-of-bounds pointer into tlv_data_is_valid(). If the bytes beyond the command buffer are addressable, they can also be copied into the advertising instance as scan response data, where the caller can read them back via MGMT_OP_GET_ADV_INSTANCE. The trigger requires CAP_NET_ADMIN in the initial user namespace; KASAN reports an 8-byte slab-out-of-bounds read. Reject commands whose length does not match the fixed header plus both advertising data lengths before parsing cp->data. Fixes: 12410572833a ("Bluetooth: Break add adv into two mgmt commands") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9065a864bc65..91d1c0d132f9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -9094,9 +9094,15 @@ static int add_ext_adv_data(struct sock *sk, struct hci_dev *hdev, void *data, struct adv_info *adv_instance; int err = 0; struct mgmt_pending_cmd *cmd; + u16 expected_len; BT_DBG("%s", hdev->name); + expected_len = struct_size(cp, data, cp->adv_data_len + cp->scan_rsp_len); + if (expected_len != data_len) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_DATA, + MGMT_STATUS_INVALID_PARAMS); + hci_dev_lock(hdev); adv_instance = hci_find_adv_instance(hdev, cp->instance); From 8b4c412e001b0c670eb937beab491af974da55b3 Mon Sep 17 00:00:00 2001 From: Jiexun Wang Date: Wed, 6 May 2026 19:43:30 +0800 Subject: [PATCH 058/697] Bluetooth: serialize accept_q access commit e83f5e24da741fa9405aeeff00b08c5ee7c37b88 upstream. bt_sock_poll() walks the accept queue without synchronization, while child teardown can unlink the same socket and drop its last reference. The unsynchronized accept queue walk has existed since the initial Bluetooth import. Protect accept_q with a dedicated lock for queue updates and polling. Also rework bt_accept_dequeue() to take temporary child references under the queue lock before dropping it and locking the child socket. Fixes: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Jann Horn Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Jiexun Wang Signed-off-by: Ren Wei Signed-off-by: Jiexun Wang Reviewed-by: Jann Horn Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/af_bluetooth.c | 87 +++++++++++++++++++++++-------- 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index d46ed9011ee5..3791915e4a0a 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -389,6 +389,7 @@ void baswap(bdaddr_t *dst, const bdaddr_t *src); struct bt_sock { struct sock sk; struct list_head accept_q; + spinlock_t accept_q_lock; /* protects accept_q */ struct sock *parent; unsigned long flags; void (*skb_msg_name)(struct sk_buff *, void *, int *); diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index bcdf086b8ca5..70e35e198075 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -154,6 +154,7 @@ struct sock *bt_sock_alloc(struct net *net, struct socket *sock, sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); + spin_lock_init(&bt_sk(sk)->accept_q_lock); sock_reset_flag(sk, SOCK_ZAPPED); @@ -214,6 +215,7 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) { const struct cred *old_cred; struct pid *old_pid; + struct bt_sock *par = bt_sk(parent); BT_DBG("parent %p, sk %p", parent, sk); @@ -224,9 +226,13 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) else lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); bt_sk(sk)->parent = parent; + spin_lock_bh(&par->accept_q_lock); + list_add_tail(&bt_sk(sk)->accept_q, &par->accept_q); + sk_acceptq_added(parent); + spin_unlock_bh(&par->accept_q_lock); + /* Copy credentials from parent since for incoming connections the * socket is allocated by the kernel. */ @@ -244,8 +250,6 @@ void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) bh_unlock_sock(sk); else release_sock(sk); - - sk_acceptq_added(parent); } EXPORT_SYMBOL(bt_accept_enqueue); @@ -254,45 +258,72 @@ EXPORT_SYMBOL(bt_accept_enqueue); */ void bt_accept_unlink(struct sock *sk) { + struct sock *parent = bt_sk(sk)->parent; + BT_DBG("sk %p state %d", sk, sk->sk_state); + spin_lock_bh(&bt_sk(parent)->accept_q_lock); list_del_init(&bt_sk(sk)->accept_q); - sk_acceptq_removed(bt_sk(sk)->parent); + sk_acceptq_removed(parent); + spin_unlock_bh(&bt_sk(parent)->accept_q_lock); bt_sk(sk)->parent = NULL; sock_put(sk); } EXPORT_SYMBOL(bt_accept_unlink); +static struct sock *bt_accept_get(struct sock *parent, struct sock *sk) +{ + struct bt_sock *bt = bt_sk(parent); + struct sock *next = NULL; + + /* accept_q is modified from child teardown paths too, so take a + * temporary reference before dropping the queue lock. + */ + spin_lock_bh(&bt->accept_q_lock); + + if (sk) { + if (bt_sk(sk)->parent != parent) + goto out; + + if (!list_is_last(&bt_sk(sk)->accept_q, &bt->accept_q)) { + next = &list_next_entry(bt_sk(sk), accept_q)->sk; + sock_hold(next); + } + } else if (!list_empty(&bt->accept_q)) { + next = &list_first_entry(&bt->accept_q, + struct bt_sock, accept_q)->sk; + sock_hold(next); + } + +out: + spin_unlock_bh(&bt->accept_q_lock); + return next; +} + struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) { - struct bt_sock *s, *n; - struct sock *sk; + struct sock *sk, *next; BT_DBG("parent %p", parent); restart: - list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) { - sk = (struct sock *)s; - + for (sk = bt_accept_get(parent, NULL); sk; sk = next) { /* Prevent early freeing of sk due to unlink and sock_kill */ - sock_hold(sk); lock_sock(sk); /* Check sk has not already been unlinked via * bt_accept_unlink() due to serialisation caused by sk locking */ - if (!bt_sk(sk)->parent) { + if (bt_sk(sk)->parent != parent) { BT_DBG("sk %p, already unlinked", sk); release_sock(sk); sock_put(sk); - /* Restart the loop as sk is no longer in the list - * and also avoid a potential infinite loop because - * list_for_each_entry_safe() is not thread safe. - */ goto restart; } + next = bt_accept_get(parent, sk); + /* sk is safely in the parent list so reduce reference count */ sock_put(sk); @@ -320,6 +351,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) sock_hold(sk); release_sock(sk); + if (next) + sock_put(next); return sk; } @@ -528,18 +561,28 @@ EXPORT_SYMBOL(bt_sock_stream_recvmsg); static inline __poll_t bt_accept_poll(struct sock *parent) { - struct bt_sock *s, *n; + struct bt_sock *bt = bt_sk(parent); + struct bt_sock *s; struct sock *sk; + __poll_t mask = 0; + + spin_lock_bh(&bt->accept_q_lock); + list_for_each_entry(s, &bt->accept_q, accept_q) { + int state; - list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) { sk = (struct sock *)s; - if (sk->sk_state == BT_CONNECTED || - (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) && - sk->sk_state == BT_CONNECT2)) - return EPOLLIN | EPOLLRDNORM; + state = READ_ONCE(sk->sk_state); + + if (state == BT_CONNECTED || + (test_bit(BT_SK_DEFER_SETUP, &bt->flags) && + state == BT_CONNECT2)) { + mask = EPOLLIN | EPOLLRDNORM; + break; + } } + spin_unlock_bh(&bt->accept_q_lock); - return 0; + return mask; } __poll_t bt_sock_poll(struct file *file, struct socket *sock, From 84bc87beb4cd77670939b446326788e4c9b3db37 Mon Sep 17 00:00:00 2001 From: Zijing Yin Date: Tue, 19 May 2026 10:26:33 -0700 Subject: [PATCH 059/697] phonet/pep: disable BH around forwarded sk_receive_skb() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dbc81608e3a653dea6cf403f20cae35468b8ab9c upstream. The networking receive path is usually run from softirq context, but protocols that take the socket lock may have packets stored in the backlog and processed later from process context. In that case release_sock() -> __release_sock() drops the slock with spin_unlock_bh() and then calls sk->sk_backlog_rcv() with bottom halves enabled. Typical sk_backlog_rcv handlers process the socket whose backlog is being drained, so the BH state at entry is irrelevant for the slocks they touch. pep_do_rcv() is different: when the inbound skb targets an existing PEP pipe, it forwards the skb to a different *child* socket via sk_receive_skb(). That helper takes the child slock with bh_lock_sock_nested(), which is just spin_lock_nested() and assumes BH is already off. The same child slock therefore ends up acquired with BH on (process path) and with BH off (softirq path): process context softirq context --------------- --------------- release_sock(listener) __netif_receive_skb() __release_sock() phonet_rcv() spin_unlock_bh() __sk_receive_skb(listener) [BH now ENABLED] [BH already disabled] sk_backlog_rcv: sk_backlog_rcv: pep_do_rcv() pep_do_rcv() sk_receive_skb(child) sk_receive_skb(child) bh_lock_sock_nested(child) bh_lock_sock_nested(child) => SOFTIRQ-ON-W => IN-SOFTIRQ-W Lockdep flags this as inconsistent lock state, and it can become a real self-deadlock if a softirq on the same CPU tries to receive to the same child socket while its slock is held in the BH-enabled path: WARNING: inconsistent lock state inconsistent {SOFTIRQ-ON-W} -> {IN-SOFTIRQ-W} usage. (slock-AF_PHONET/1){+.?.}-{3:3}, at: __sk_receive_skb+0x1cf/0x900 __sk_receive_skb net/core/sock.c:563 sk_receive_skb include/net/sock.h:2022 [inline] pep_do_rcv net/phonet/pep.c:675 sk_backlog_rcv include/net/sock.h:1190 __release_sock net/core/sock.c:3216 release_sock net/core/sock.c:3815 pep_sock_accept net/phonet/pep.c:879 Wrap the forwarded sk_receive_skb() in local_bh_disable() / local_bh_enable() so the child slock is always acquired with BH off. local_bh_disable() nests safely on the softirq path. Discovered via in-house syzkaller fuzzing; the same root cause also on the linux-6.1.y syzbot dashboard as extid 44f0626dd6284f02663c. Reproduced under KASAN + LOCKDEP + PROVE_LOCKING, reproducer: https://pastebin.com/A3t8xzCR Fixes: 9641458d3ec4 ("Phonet: Pipe End Point for Phonet Pipes protocol") Link: https://syzkaller.appspot.com/bug?extid=44f0626dd6284f02663c Cc: stable@vger.kernel.org Signed-off-by: Zijing Yin Acked-by: Rémi Denis-Courmont Reported-by: syzbot+9f4a135646b66c509935@syzkaller.appspotmail.com Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260519172635.86304-1-yzjaurora@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/phonet/pep.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 4db564d9d522..058a16c423ae 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -671,8 +671,23 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb) /* Look for an existing pipe handle */ sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle); - if (sknode) - return sk_receive_skb(sknode, skb, 1); + if (sknode) { + int rc; + + /* pep_do_rcv() runs from two contexts: from softirq via + * phonet_rcv() -> __sk_receive_skb() with BH disabled, + * and from process context via + * release_sock() -> __release_sock(), which drops + * the listener slock with spin_unlock_bh() before draining + * the backlog. The child pipe slock is taken below via + * bh_lock_sock_nested(), which does not itself disable BH, so + * disable BH here to keep both acquire contexts consistent. + */ + local_bh_disable(); + rc = sk_receive_skb(sknode, skb, 1); + local_bh_enable(); + return rc; + } switch (hdr->message_id) { case PNS_PEP_CONNECT_REQ: From 3d4ef05266ab16d8ef7dd21658a557801eb78704 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 20 May 2026 20:43:20 +0200 Subject: [PATCH 060/697] net: bcmgenet: keep RBUF EEE/PM disabled commit 9a1730245e416d11ad5c0f2c100061d61cc43f60 upstream. Setting RBUF_EEE_EN | RBUF_PM_EN in RBUF_ENERGY_CTRL breaks the RX path on GENET hardware once MAC EEE becomes active. RX traffic stops flowing while the link stays up and the usual descriptor/RX error counters remain quiet. In that state the MAC still accepts frames (rbuf_ovflow_cnt keeps climbing) but RBUF no longer forwards them to DMA, so rx_packets is no longer incremented at the netdev level. On some boards the corruption ends up as a paging fault in skb_release_data via bcmgenet_rx_poll on an LPI exit. Reproduced on Pi 4B (BCM2711 + BCM54213PE) and confirmed by Florian Fainelli on an internal Broadcom 4908-family board with the same crash signature. RBUF_PM_EN is not publicly documented. This shows up more often now that phy_support_eee() enables EEE by default, but it also affects older kernels as soon as TX LPI is turned on via ethtool, so it is not specific to recent changes. Always clear RBUF_EEE_EN | RBUF_PM_EN in bcmgenet_eee_enable_set so the bits stay off across resets. UMAC and TBUF setup is left alone so TX-side EEE keeps working. Link: https://github.com/raspberrypi/linux/issues/7304 Fixes: 6ef398ea60d9 ("net: bcmgenet: add EEE support") Cc: stable@vger.kernel.org Signed-off-by: Nicolai Buchwitz Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260520184320.652053-1-nb@tipi-net.de Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 63cdf6d9d077..abf6a6451cb1 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1369,13 +1369,12 @@ void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) reg &= ~(TBUF_EEE_EN | TBUF_PM_EN); bcmgenet_writel(reg, priv->base + off); - /* Do the same for thing for RBUF */ + /* RBUF EEE/PM can break the RX path on GENET. Keep it disabled. */ reg = bcmgenet_rbuf_readl(priv, RBUF_ENERGY_CTRL); - if (enable) - reg |= RBUF_EEE_EN | RBUF_PM_EN; - else + if (reg & (RBUF_EEE_EN | RBUF_PM_EN)) { reg &= ~(RBUF_EEE_EN | RBUF_PM_EN); - bcmgenet_rbuf_writel(priv, reg, RBUF_ENERGY_CTRL); + bcmgenet_rbuf_writel(priv, reg, RBUF_ENERGY_CTRL); + } if (!enable && priv->clk_eee_enabled) { clk_disable_unprepare(priv->clk_eee); From 49eff79967fd6ea45a9d028710bb852c950baef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nerijus=20Bend=C5=BEi=C5=ABnas?= Date: Sat, 16 May 2026 18:02:51 +0300 Subject: [PATCH 061/697] net: phy: skip EEE advertisement write when autoneg is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 960e77ce14a83ef7f226e8e4b4d75765633ba48b upstream. genphy_c45_an_config_eee_aneg() writes the EEE advertisement to the auto-negotiation device's MMD register space (MDIO_MMD_AN, register MDIO_AN_EEE_ADV). These registers are read by the link partner only during auto-negotiation, so writing them while autoneg is disabled cannot influence the link. On some PHYs (e.g. Broadcom BCM54213PE) the write nevertheless reaches the chip and disturbs the receive datapath. Concretely, running ethtool -s eth0 speed 100 duplex full autoneg off ethtool --set-eee eth0 eee off leaves eth0 with TX working and RX completely silent on a Raspberry Pi 4 / CM4 board (bcmgenet + BCM54213PE in rgmii-rxid). Switching back to autoneg recovers the link. Prior to commit f26a29a038ee ("net: phy: ensure that genphy_c45_an_config_eee_aneg() sees new value of phydev->eee_cfg.eee_enabled"), the disable path was effectively a no-op because the helper read the stale eee_cfg.eee_enabled, so the underlying PHY behavior never surfaced. Bisected on rpi-6.12.y between commits 83943264 (good) and effcbc88 (bad) to f26a29a038ee. Fixes: f26a29a038ee ("net: phy: ensure that genphy_c45_an_config_eee_aneg() sees new value of phydev->eee_cfg.eee_enabled") Cc: stable@vger.kernel.org Signed-off-by: Nerijus Bendžiūnas Reviewed-by: Nicolai Buchwitz Tested-by: Nicolai Buchwitz Link: https://patch.msgid.link/20260516150251.879680-1-nerijus.bendziunas@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/phy/phy-c45.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c index 61670be0f095..d2e36e460cc7 100644 --- a/drivers/net/phy/phy-c45.c +++ b/drivers/net/phy/phy-c45.c @@ -939,6 +939,14 @@ EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities); */ int genphy_c45_an_config_eee_aneg(struct phy_device *phydev) { + /* Writing MMD AN advertisements while autoneg is disabled has no + * effect on link-partner negotiation, but on some PHYs (e.g. the + * Broadcom BCM54213PE) the write itself disturbs the receive + * datapath. Skip it. + */ + if (phydev->autoneg == AUTONEG_DISABLE) + return 0; + if (!phydev->eee_cfg.eee_enabled) { __ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {}; From 1604a2d68414aa4cc34faac0b7faa9c14455e8d3 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Wed, 13 May 2026 12:22:26 +0100 Subject: [PATCH 062/697] net/mlx5e: Fix use-after-free in mlx5e_tx_reporter_timeout_recover commit 7d260c5d2d89eb2c8c528d54b576b3aae3e20231 upstream. mlx5e_tx_reporter_timeout_recover() accesses sq->netdev after mlx5e_safe_reopen_channels() has torn down and freed the channel (and its embedded SQs). Replace the three sq->netdev references with priv->netdev which is safe because priv outlives channel teardown. The netdev_err() call already used priv->netdev for this reason; make the trylock/unlock and health_channel_eq_recover calls consistent. This fixes the following KASAN splat: BUG: KASAN: use-after-free in mlx5e_tx_reporter_timeout_recover+0x1dd/0x360 [mlx5_core] Read of size 8 at addr ffff889860ed0b28 by task kworker/u113:2/5277 Call Trace: mlx5e_tx_reporter_timeout_recover+0x1dd/0x360 [mlx5_core] devlink_health_reporter_recover+0xa2/0x150 devlink_health_report+0x254/0x7c0 mlx5e_reporter_tx_timeout+0x297/0x380 [mlx5_core] mlx5e_tx_timeout_work+0x109/0x170 [mlx5_core] process_one_work+0x677/0xf20 worker_thread+0x51f/0xd90 kthread+0x3a5/0x810 ret_from_fork+0x208/0x400 ret_from_fork_asm+0x1a/0x30 Fixes: 83ac0304a2d7 ("net/mlx5e: Fix deadlocks between devlink and netdev instance locks") Cc: stable@vger.kernel.org Reviewed-by: Cosmin Ratiu Reviewed-by: Tariq Toukan Signed-off-by: Matt Fleming Link: https://patch.msgid.link/20260513112226.140512-1-matt@readmodwrite.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c index d6ace2b6fc1d..198033620c83 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c @@ -159,13 +159,13 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx) * channels are being closed for other reason and this work is not * relevant anymore. */ - while (!netdev_trylock(sq->netdev)) { + while (!netdev_trylock(priv->netdev)) { if (!test_bit(MLX5E_STATE_CHANNELS_ACTIVE, &priv->state)) return 0; msleep(20); } - err = mlx5e_health_channel_eq_recover(sq->netdev, eq, sq->cq.ch_stats); + err = mlx5e_health_channel_eq_recover(priv->netdev, eq, sq->cq.ch_stats); if (!err) { to_ctx->status = 0; /* this sq recovered */ goto out; @@ -185,7 +185,7 @@ static int mlx5e_tx_reporter_timeout_recover(void *ctx) "mlx5e_safe_reopen_channels failed recovering from a tx_timeout, err(%d).\n", err); out: - netdev_unlock(sq->netdev); + netdev_unlock(priv->netdev); return err; } From f8a5a76b4a683043c6eff2a060bcaa17f9316ad5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 13 May 2026 21:37:39 -0400 Subject: [PATCH 063/697] net: ifb: report ethtool stats over num_tx_queues commit 5db89c99566fc4728cc92e941d8e1975711e24b5 upstream. ifb_dev_init() allocates dp->tx_private to dev->num_tx_queues entries via kzalloc_objs(*txp, dev->num_tx_queues). Both IFB per-queue RX and TX stats live in those entries: ifb_xmit() updates txp->rx_stats using the skb queue mapping, ifb_ri_tasklet() updates txp->tx_stats, and ifb_stats64() aggregates both over dev->num_tx_queues. The ethtool stats callbacks instead size and walk the per-queue stats with dev->real_num_rx_queues and dev->real_num_tx_queues. With an asymmetric device where the RX queue count exceeds the TX queue count, for example: ip link add name ifb10 numtxqueues 1 numrxqueues 8 type ifb ethtool -S ifb10 ifb_get_ethtool_stats() indexes past the tx_private allocation and copies adjacent slab data through ETHTOOL_GSTATS. Use dev->num_tx_queues consistently for the stats strings, the stats count, and the stats data walks. This reports one RX stats group and one TX stats group for each backing ifb_q_private entry, which is the queue set IFB can actually populate. Reproduced under UML+KASAN at v7.1-rc2: BUG: KASAN: slab-out-of-bounds in ifb_fill_stats_data+0x3c/0xae Read of size 8 at addr 0000000062dbd228 by task ethtool/36 ifb_fill_stats_data+0x3c/0xae ifb_get_ethtool_stats+0xc0/0x129 __dev_ethtool+0x1ca5/0x363c dev_ethtool+0x123/0x1b3 dev_ioctl+0x56c/0x744 sock_do_ioctl+0x15f/0x1b2 sock_ioctl+0x4d5/0x50a sys_ioctl+0xd8b/0xde9 With the patch applied, the same UML+KASAN repro is silent and ethtool -S ifb10 reports only the stats backed by the single allocated tx_private entry. Fixes: a21ee5b2fcb8 ("net: ifb: support ethtools stats") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260514013739.3549624-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ifb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index d3dc0914450a..0534d2471e61 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -211,12 +211,12 @@ static void ifb_get_strings(struct net_device *dev, u32 stringset, u8 *buf) switch (stringset) { case ETH_SS_STATS: - for (i = 0; i < dev->real_num_rx_queues; i++) + for (i = 0; i < dev->num_tx_queues; i++) for (j = 0; j < IFB_Q_STATS_LEN; j++) ethtool_sprintf(&p, "rx_queue_%u_%.18s", i, ifb_q_stats_desc[j].desc); - for (i = 0; i < dev->real_num_tx_queues; i++) + for (i = 0; i < dev->num_tx_queues; i++) for (j = 0; j < IFB_Q_STATS_LEN; j++) ethtool_sprintf(&p, "tx_queue_%u_%.18s", i, ifb_q_stats_desc[j].desc); @@ -229,8 +229,7 @@ static int ifb_get_sset_count(struct net_device *dev, int sset) { switch (sset) { case ETH_SS_STATS: - return IFB_Q_STATS_LEN * (dev->real_num_rx_queues + - dev->real_num_tx_queues); + return IFB_Q_STATS_LEN * dev->num_tx_queues * 2; default: return -EOPNOTSUPP; } @@ -262,12 +261,12 @@ static void ifb_get_ethtool_stats(struct net_device *dev, struct ifb_q_private *txp; int i; - for (i = 0; i < dev->real_num_rx_queues; i++) { + for (i = 0; i < dev->num_tx_queues; i++) { txp = dp->tx_private + i; ifb_fill_stats_data(&data, &txp->rx_stats); } - for (i = 0; i < dev->real_num_tx_queues; i++) { + for (i = 0; i < dev->num_tx_queues; i++) { txp = dp->tx_private + i; ifb_fill_stats_data(&data, &txp->tx_stats); } From dac025c4e8f9c5cf9467eeac8be4639469aa5ac5 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Fri, 15 May 2026 14:31:03 +0000 Subject: [PATCH 064/697] net: pse-pd: fix sign on -ENOENT check in of_load_pse_pis() commit 33d35975cbead3fa6b738ee57e5e45e14fbe0886 upstream. of_count_phandle_with_args() returns the count on success and a negative errno on failure, including -ENOENT when the "pairsets" property is absent. The existing comparison in of_load_pse_pis() checks against ENOENT (positive 2) instead of -ENOENT, so the branch is taken for any error return: legitimate DTs that omit "pairsets" trigger a spurious "wrong number of pairsets" error and probe fails with -EINVAL. Compare against -ENOENT so a missing "pairsets" property is correctly treated as "this PI has no pairsets, continue". Fixes: 9be9567a7c59 ("net: pse-pd: Add support for PSE PIs") Cc: stable@vger.kernel.org Signed-off-by: Jonas Jelonek Acked-by: Oleksij Rempel Link: https://patch.msgid.link/20260515143103.1721888-1-jelonek.jonas@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/pse-pd/pse_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 23eb3c9d0bcd..af354483d191 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -210,7 +210,7 @@ static int of_load_pse_pis(struct pse_controller_dev *pcdev) ret = of_load_pse_pi_pairsets(node, &pi, ret); if (ret) goto out; - } else if (ret != ENOENT) { + } else if (ret != -ENOENT) { dev_err(pcdev->dev, "error: wrong number of pairsets. Should be 1 or 2, got %d (%pOF)\n", ret, node); From 57b0ac5e1b46f1f0338dff392ef2092e2871b412 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Wed, 13 May 2026 15:57:17 +0800 Subject: [PATCH 065/697] netfilter: ip6t_hbh: reject oversized option lists commit 4322dcde6b4173c2d8e8e6118ed290794263bcc8 upstream. struct ip6t_opts stores at most IP6T_OPTS_OPTSNR option descriptors, but hbh_mt6_check() does not reject larger optsnr values supplied from userspace. Validate optsnr in the rule setup path so only match data that fits the fixed-size opts array can be installed. This follows the existing xtables pattern of rejecting invalid user-provided counts in checkentry() and keeps the packet matching path unchanged. `struct ip6t_opts` has a fixed `opts[IP6T_OPTS_OPTSNR]` array, where `IP6T_OPTS_OPTSNR` is 16, then off-by-one array access is possible: [ 137.924693][ T8692] UBSAN: array-index-out-of-bounds in ../net/ipv6/netfilter/ip6t_hbh.c:110:29 [ 137.926167][ T8692] index 16 is out of range for type '__u16 [16]' Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/ipv6/netfilter/ip6t_hbh.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index e7a3fb9355ee..450dd53846a2 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -168,6 +168,10 @@ static int hbh_mt6_check(const struct xt_mtchk_param *par) pr_debug("unknown flags %X\n", optsinfo->invflags); return -EINVAL; } + if (optsinfo->optsnr > IP6T_OPTS_OPTSNR) { + pr_debug("too many supported opts specified\n"); + return -EINVAL; + } if (optsinfo->flags & IP6T_OPTS_NSTRICT) { pr_debug("Not strict - not implemented"); From 15d464265120ab9818bd673af301deee09bedab2 Mon Sep 17 00:00:00 2001 From: Haoze Xie Date: Fri, 15 May 2026 11:19:02 +0800 Subject: [PATCH 066/697] netfilter: nf_queue: hold bridge skb->dev while queued commit e196115ec330a18de415bdb9f5071aa9f08e53ce upstream. br_pass_frame_up() rewrites skb->dev from the ingress port to the bridge master before queueing bridge LOCAL_IN packets. NFQUEUE only holds references on state.in/out and bridge physdevs, so a queued bridge packet can retain a freed bridge master in skb->dev until reinjection. When the verdict is reinjected later, br_netif_receive_skb() re-enters the receive path with skb->dev still pointing at the freed bridge master, triggering a use-after-free. Store skb->dev in the queue entry, hold a reference on it for the queue lifetime, and use the saved device when dropping queued packets during NETDEV_DOWN handling. Fixes: ac2863445686 ("netfilter: bridge: add nf_afinfo to enable queuing to userspace") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Haoze Xie Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- include/net/netfilter/nf_queue.h | 1 + net/netfilter/nf_queue.c | 4 +++- net/netfilter/nfnetlink_queue.c | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index d17035d14d96..3978c3174cdb 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -14,6 +14,7 @@ struct nf_queue_entry { struct list_head list; struct rhash_head hash_node; struct sk_buff *skb; + struct net_device *skb_dev; unsigned int id; unsigned int hook_index; /* index in hook_entries->hook[] */ #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 7f12e56e6e52..dd416c8532c5 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -60,6 +60,7 @@ static void nf_queue_entry_release_refs(struct nf_queue_entry *entry) struct nf_hook_state *state = &entry->state; /* Release those devices we held, or Alexey will kill me. */ + dev_put(entry->skb_dev); dev_put(state->in); dev_put(state->out); if (state->sk) @@ -101,6 +102,7 @@ bool nf_queue_entry_get_refs(struct nf_queue_entry *entry) if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt)) return false; + dev_hold(entry->skb_dev); dev_hold(state->in); dev_hold(state->out); @@ -201,11 +203,11 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, *entry = (struct nf_queue_entry) { .skb = skb, + .skb_dev = skb->dev, .state = *state, .hook_index = index, .size = sizeof(*entry) + route_key_size, }; - __nf_queue_entry_init_physdevs(entry); if (!nf_queue_entry_get_refs(entry)) { diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index fe5942535245..d42e8ac3062f 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1198,6 +1198,8 @@ dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) if (physinif == ifindex || physoutif == ifindex) return 1; #endif + if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) + return 1; if (entry->state.in) if (entry->state.in->ifindex == ifindex) return 1; From 952e988163c2ab9939c3db9f0f8e77af6a1bb436 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 12 May 2026 16:50:01 +0800 Subject: [PATCH 067/697] netfilter: ipset: stop hash:* range iteration at end commit 0d3a282ab5f165fc207ff49ea5b6ad8f54616bd6 upstream. The following hash set variants: hash:ip,mark hash:ip,port hash:ip,port,ip hash:ip,port,net iterate IPv4 ranges with a 32-bit iterator. The iterator must stop once the last address in the requested range has been processed. Advancing it once more can move the traversal state past the end of the request, so a later retry may continue from an unintended position. Handle the iterator increment explicitly at the end of the loop and stop once the upper bound has been processed. This keeps the existing retry behaviour intact for valid ranges while preventing traversal from continuing past the original boundary. Fixes: 48596a8ddc46 ("netfilter: ipset: Fix adding an IPv4 range containing more than 2^31 addresses") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Nan Li Signed-off-by: Ren Wei Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/ipset/ip_set_hash_ipmark.c | 6 +++++- net/netfilter/ipset/ip_set_hash_ipport.c | 5 ++++- net/netfilter/ipset/ip_set_hash_ipportip.c | 5 ++++- net/netfilter/ipset/ip_set_hash_ipportnet.c | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_ipmark.c b/net/netfilter/ipset/ip_set_hash_ipmark.c index a22ec1a6f6ec..e26ca2a370e3 100644 --- a/net/netfilter/ipset/ip_set_hash_ipmark.c +++ b/net/netfilter/ipset/ip_set_hash_ipmark.c @@ -150,7 +150,7 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[], if (retried) ip = ntohl(h->next.ip); - for (; ip <= ip_to; ip++, i++) { + for (; ip <= ip_to; i++) { e.ip = htonl(ip); if (i > IPSET_MAX_RANGE) { hash_ipmark4_data_next(&h->next, &e); @@ -162,6 +162,10 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; ret = 0; + + if (ip == ip_to) + break; + ip++; } return ret; } diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index e977b5a9c48d..41ca24a22a02 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -186,7 +186,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], if (retried) ip = ntohl(h->next.ip); - for (; ip <= ip_to; ip++) { + for (; ip <= ip_to;) { p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) : port; for (; p <= port_to; p++, i++) { @@ -203,6 +203,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], ret = 0; } + if (ip == ip_to) + break; + ip++; } return ret; } diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 39a01934b153..b9ac2efaa15c 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -182,7 +182,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], if (retried) ip = ntohl(h->next.ip); - for (; ip <= ip_to; ip++) { + for (; ip <= ip_to;) { p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) : port; for (; p <= port_to; p++, i++) { @@ -199,6 +199,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], ret = 0; } + if (ip == ip_to) + break; + ip++; } return ret; } diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 5c6de605a9fb..2d6652d43199 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -274,7 +274,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], p = port; ip2 = ip2_from; } - for (; ip <= ip_to; ip++) { + for (; ip <= ip_to;) { e.ip = htonl(ip); for (; p <= port_to; p++) { e.port = htons(p); @@ -298,6 +298,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], ip2 = ip2_from; } p = port; + if (ip == ip_to) + break; + ip++; } return ret; } From 689bbf48c1f45130086ae1c46ab83ea4c753c601 Mon Sep 17 00:00:00 2001 From: Yizhou Zhao Date: Tue, 12 May 2026 01:30:41 +0800 Subject: [PATCH 068/697] netfilter: nft_inner: Fix IPv6 inner_thoff desync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b6a91f68ebfed9c38e0e9150f58a9b85da07181c upstream. In nft_inner_parse_l2l3(), when processing inner IPv6 packets, ipv6_find_hdr() correctly computes the transport header offset traversing all extension headers, but the result is immediately overwritten with nhoff + sizeof(_ip6h) (40 bytes), which only accounts for the IPv6 base header. This creates a desync between inner_thoff (wrong — points to extension header start) and l4proto (correct — e.g., IPPROTO_TCP), enabling transport header forgery and potential firewall bypass. This issue affects stable versions from Linux 6.2. For comparison, the normal (non-inner) IPv6 path correctly preserves ipv6_find_hdr()'s result. Removing the incorrect overwrite ensures that ipv6_find_hdr()'s calculated transport header offset is preserved, thereby fixing the desynchronization. Fixes: 3a07327d10a0 ("netfilter: nft_inner: support for inner tunnel header matching") Cc: stable@vger.kernel.org Reported-by: Yizhou Zhao Reported-by: Yuxiang Yang Reported-by: Xuewei Feng Reported-by: Qi Li Reported-by: Ke Xu Assisted-by: GLM:5.1 Z.ai Signed-off-by: Yizhou Zhao Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nft_inner.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index c4569d4b9228..1b3e7a976f56 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -163,7 +163,6 @@ static int nft_inner_parse_l2l3(const struct nft_inner *priv, return -1; if (fragoff == 0) { - thoff = nhoff + sizeof(_ip6h); ctx->flags |= NFT_PAYLOAD_CTX_INNER_TH; ctx->inner_thoff = thoff; ctx->l4proto = l4proto; From 6e73ec10b2a32b1126423a71f2c4247041048d81 Mon Sep 17 00:00:00 2001 From: Samuele Mariotti Date: Thu, 21 May 2026 09:53:57 -0400 Subject: [PATCH 069/697] sched_ext: Fix missing warning in scx_set_task_state() default case [ Upstream commit b905ee77d5f557a83a485b4146210f54f13365fc ] In scx_set_task_state(), the default case was setting the warn flag, but then returning immediately. This is problematic because the only purpose of the warn flag is to trigger WARN_ONCE, but the early return prevented it from ever firing, leaving invalid task states undetected and untraced. To fix this, a WARN_ONCE call is now added directly in the default case. The fix addresses two aspects: - Guarantees the invalid task states are properly logged and traced. - Provides a distinct warning message ("sched_ext: Invalid task state") specifically for states outside the defined scx_task_state enum values, making it easier to distinguish from other transition warnings. This ensures proper detection and reporting of invalid states. Signed-off-by: Samuele Mariotti Signed-off-by: Paolo Valente Reviewed-by: Andrea Righi Signed-off-by: Tejun Heo Stable-dep-of: 9a415cc53711 ("sched_ext: Avoid UAF in scx_root_enable_workfn() init failure path") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/sched/ext.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 3029e5b8f9a5..2cfd321d9236 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -2800,7 +2800,8 @@ static void scx_set_task_state(struct task_struct *p, enum scx_task_state state) warn = prev_state != SCX_TASK_READY; break; default: - warn = true; + WARN_ONCE(1, "sched_ext: Invalid task state %d -> %d for %s[%d]", + prev_state, state, p->comm, p->pid); return; } From 45c7c4e3db8b700307313c035ea08be829a7f21b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 21 May 2026 09:53:58 -0400 Subject: [PATCH 070/697] sched_ext: Avoid UAF in scx_root_enable_workfn() init failure path [ Upstream commit 9a415cc53711f2238e0f0ca8a6bcc796c003b127 ] In scx_root_enable_workfn(), put_task_struct(p) is called before scx_error() dereferences p->comm and p->pid. If the iterator's reference is the last drop, the task is freed synchronously and the deref becomes a UAF. Move put_task_struct() past scx_error(). Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260511214031.AF5E9C2BCB0@smtp.kernel.org/ Fixes: f0e1a0643a59 ("sched_ext: Implement BPF extensible scheduler class") Cc: stable@vger.kernel.org # v6.12+ Signed-off-by: Tejun Heo [ kept `scx_init_task()` call site instead of `__scx_init_task()`/`task_rq_lock` ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/sched/ext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c index 2cfd321d9236..7b750bf42698 100644 --- a/kernel/sched/ext.c +++ b/kernel/sched/ext.c @@ -4812,10 +4812,10 @@ static void scx_enable_workfn(struct kthread_work *work) ret = scx_init_task(p, task_group(p), false); if (ret) { - put_task_struct(p); scx_task_iter_stop(&sti); scx_error(sch, "ops.init_task() failed (%d) for %s[%d]", ret, p->comm, p->pid); + put_task_struct(p); goto err_disable_unlock_all; } From 52cc572c95655ad71cdde4e494abf770efe9e233 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 21 May 2026 08:58:10 -0400 Subject: [PATCH 071/697] tracing: fprobe: Remove unused local variable [ Upstream commit 90e69d291d195d35215b578d210fd3ce0e5a3f42 ] The 'ret' local variable in fprobe_remove_node_in_module() was used for checking the error state in the loop, but commit dfe0d675df82 ("tracing: fprobe: use rhltable for fprobe_ip_table") removed the loop. So we don't need it anymore. Link: https://lore.kernel.org/all/175867358989.600222.6175459620045800878.stgit@devnote2/ Fixes: e5a4cc28a052 ("tracing: fprobe: use rhltable for fprobe_ip_table") Signed-off-by: Masami Hiramatsu (Google) Acked-by: Menglong Dong Stable-dep-of: 0ac0058a74ac ("tracing/fprobe: Check the same type fprobe on table as the unregistered one") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/trace/fprobe.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 8fa5bff2c26f..51003d85933d 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -561,8 +561,6 @@ static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long ad static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, struct fprobe_addr_list *alist) { - int ret = 0; - if (!within_module(node->addr, mod)) return; if (delete_fprobe_node(node)) @@ -571,8 +569,7 @@ static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist * If failed to update alist, just continue to update hlist. * Therefore, at list user handler will not hit anymore. */ - if (!ret) - ret = fprobe_addr_list_add(alist, node->addr); + fprobe_addr_list_add(alist, node->addr); } /* Handle module unloading to manage fprobe_ip_table. */ From bb92f356d2b73bc65ea20919a0e39efdf538f5cd Mon Sep 17 00:00:00 2001 From: Menglong Dong Date: Thu, 21 May 2026 08:58:11 -0400 Subject: [PATCH 072/697] tracing: fprobe: use ftrace if CONFIG_DYNAMIC_FTRACE_WITH_ARGS [ Upstream commit cd06078a38aaedfebbf8fa0c009da0f99f4473fb ] For now, we will use ftrace for the fprobe if fp->exit_handler not exists and CONFIG_DYNAMIC_FTRACE_WITH_REGS is enabled. However, CONFIG_DYNAMIC_FTRACE_WITH_REGS is not supported by some arch, such as arm. What we need in the fprobe is the function arguments, so we can use ftrace for fprobe if CONFIG_DYNAMIC_FTRACE_WITH_ARGS is enabled. Therefore, use ftrace if CONFIG_DYNAMIC_FTRACE_WITH_REGS or CONFIG_DYNAMIC_FTRACE_WITH_ARGS enabled. Link: https://lore.kernel.org/all/20251103063434.47388-1-dongml2@chinatelecom.cn/ Signed-off-by: Menglong Dong Signed-off-by: Masami Hiramatsu (Google) Stable-dep-of: 0ac0058a74ac ("tracing/fprobe: Check the same type fprobe on table as the unregistered one") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/trace/fprobe.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 51003d85933d..574f2886cc9e 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -45,6 +45,7 @@ static struct hlist_head fprobe_table[FPROBE_TABLE_SIZE]; static struct rhltable fprobe_ip_table; static DEFINE_MUTEX(fprobe_mutex); +static struct fgraph_ops fprobe_graph_ops; static u32 fprobe_node_hashfn(const void *data, u32 len, u32 seed) { @@ -259,7 +260,7 @@ static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent return ret; } -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS +#if defined(CONFIG_DYNAMIC_FTRACE_WITH_ARGS) || defined(CONFIG_DYNAMIC_FTRACE_WITH_REGS) /* ftrace_ops callback, this processes fprobes which have only entry_handler. */ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct ftrace_regs *fregs) @@ -300,7 +301,7 @@ NOKPROBE_SYMBOL(fprobe_ftrace_entry); static struct ftrace_ops fprobe_ftrace_ops = { .func = fprobe_ftrace_entry, - .flags = FTRACE_OPS_FL_SAVE_REGS, + .flags = FTRACE_OPS_FL_SAVE_ARGS, }; static int fprobe_ftrace_active; @@ -341,6 +342,15 @@ static bool fprobe_is_ftrace(struct fprobe *fp) { return !fp->exit_handler; } + +#ifdef CONFIG_MODULES +static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, + int reset) +{ + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); + ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, remove, reset); +} +#endif #else static int fprobe_ftrace_add_ips(unsigned long *addrs, int num) { @@ -355,7 +365,15 @@ static bool fprobe_is_ftrace(struct fprobe *fp) { return false; } + +#ifdef CONFIG_MODULES +static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, + int reset) +{ + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); +} #endif +#endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ /* fgraph_ops callback, this processes fprobes which have exit_handler. */ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, @@ -601,14 +619,8 @@ static int fprobe_module_callback(struct notifier_block *nb, } while (node == ERR_PTR(-EAGAIN)); rhashtable_walk_exit(&iter); - if (alist.index > 0) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, - alist.addrs, alist.index, 1, 0); -#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS - ftrace_set_filter_ips(&fprobe_ftrace_ops, - alist.addrs, alist.index, 1, 0); -#endif - } + if (alist.index > 0) + fprobe_set_ips(alist.addrs, alist.index, 1, 0); mutex_unlock(&fprobe_mutex); kfree(alist.addrs); From f0ad68d2f0adbeae50140c944d5676a58bf92aa6 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 21 May 2026 08:58:12 -0400 Subject: [PATCH 073/697] tracing/fprobe: Avoid kcalloc() in rcu_read_lock section [ Upstream commit aa72812b49104bb5a38272fc9541feb62ca6fd32 ] fprobe_remove_node_in_module() is called under RCU read locked, but this invokes kcalloc() if there are more than 8 fprobes installed on the module. Sashiko warns it because kcalloc() can sleep [1]. [1] https://sashiko.dev/#/patchset/177552432201.853249.5125045538812833325.stgit%40mhiramat.tok.corp.google.com To fix this issue, expand the batch size to 128 and do not expand the fprobe_addr_list, but just cancel walking on fprobe_ip_table, update fgraph/ftrace_ops and retry the loop again. Link: https://lore.kernel.org/all/177669367206.132053.1493637946869032744.stgit@mhiramat.tok.corp.google.com/ Fixes: 0de4c70d04a4 ("tracing: fprobe: use rhltable for fprobe_ip_table") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) Stable-dep-of: 0ac0058a74ac ("tracing/fprobe: Check the same type fprobe on table as the unregistered one") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/trace/fprobe.c | 92 +++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 574f2886cc9e..37a4352efb4d 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -344,11 +344,10 @@ static bool fprobe_is_ftrace(struct fprobe *fp) } #ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, - int reset) +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); - ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, remove, reset); + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); + ftrace_set_filter_ips(&fprobe_ftrace_ops, ips, cnt, 1, 0); } #endif #else @@ -367,10 +366,9 @@ static bool fprobe_is_ftrace(struct fprobe *fp) } #ifdef CONFIG_MODULES -static void fprobe_set_ips(unsigned long *ips, unsigned int cnt, int remove, - int reset) +static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { - ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, remove, reset); + ftrace_set_filter_ips(&fprobe_graph_ops.ops, ips, cnt, 1, 0); } #endif #endif /* !CONFIG_DYNAMIC_FTRACE_WITH_ARGS && !CONFIG_DYNAMIC_FTRACE_WITH_REGS */ @@ -543,7 +541,7 @@ static void fprobe_graph_remove_ips(unsigned long *addrs, int num) #ifdef CONFIG_MODULES -#define FPROBE_IPS_BATCH_INIT 8 +#define FPROBE_IPS_BATCH_INIT 128 /* instruction pointer address list */ struct fprobe_addr_list { int index; @@ -551,45 +549,24 @@ struct fprobe_addr_list { unsigned long *addrs; }; -static int fprobe_addr_list_add(struct fprobe_addr_list *alist, unsigned long addr) +static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, + struct fprobe_addr_list *alist) { - unsigned long *addrs; - - /* Previously we failed to expand the list. */ - if (alist->index == alist->size) - return -ENOSPC; - - alist->addrs[alist->index++] = addr; - if (alist->index < alist->size) + if (!within_module(node->addr, mod)) return 0; - /* Expand the address list */ - addrs = kcalloc(alist->size * 2, sizeof(*addrs), GFP_KERNEL); - if (!addrs) - return -ENOMEM; - - memcpy(addrs, alist->addrs, alist->size * sizeof(*addrs)); - alist->size *= 2; - kfree(alist->addrs); - alist->addrs = addrs; + if (delete_fprobe_node(node)) + return 0; + /* If no address list is available, we can't track this address. */ + if (!alist->addrs) + return 0; + alist->addrs[alist->index++] = node->addr; + if (alist->index == alist->size) + return -ENOSPC; return 0; } -static void fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, - struct fprobe_addr_list *alist) -{ - if (!within_module(node->addr, mod)) - return; - if (delete_fprobe_node(node)) - return; - /* - * If failed to update alist, just continue to update hlist. - * Therefore, at list user handler will not hit anymore. - */ - fprobe_addr_list_add(alist, node->addr); -} - /* Handle module unloading to manage fprobe_ip_table. */ static int fprobe_module_callback(struct notifier_block *nb, unsigned long val, void *data) @@ -598,29 +575,50 @@ static int fprobe_module_callback(struct notifier_block *nb, struct fprobe_hlist_node *node; struct rhashtable_iter iter; struct module *mod = data; + bool retry; if (val != MODULE_STATE_GOING) return NOTIFY_DONE; alist.addrs = kcalloc(alist.size, sizeof(*alist.addrs), GFP_KERNEL); - /* If failed to alloc memory, we can not remove ips from hash. */ - if (!alist.addrs) - return NOTIFY_DONE; + /* + * If failed to alloc memory, ftrace_ops will not be able to remove ips from + * hash, but we can still remove nodes from fprobe_ip_table, so we can avoid + * the potential wrong callback. So just print a warning here and try to + * continue without address list. + */ + WARN_ONCE(!alist.addrs, + "Failed to allocate memory for fprobe_addr_list, ftrace_ops will not be updated"); mutex_lock(&fprobe_mutex); +again: + retry = false; + alist.index = 0; rhltable_walk_enter(&fprobe_ip_table, &iter); do { rhashtable_walk_start(&iter); while ((node = rhashtable_walk_next(&iter)) && !IS_ERR(node)) - fprobe_remove_node_in_module(mod, node, &alist); + if (fprobe_remove_node_in_module(mod, node, &alist) < 0) { + retry = true; + break; + } rhashtable_walk_stop(&iter); - } while (node == ERR_PTR(-EAGAIN)); + } while (node == ERR_PTR(-EAGAIN) && !retry); rhashtable_walk_exit(&iter); + /* Remove any ips from hash table(s) */ + if (alist.index > 0) { + fprobe_remove_ips(alist.addrs, alist.index); + /* + * If we break rhashtable walk loop except for -EAGAIN, we need + * to restart looping from start for safety. Anyway, this is + * not a hotpath. + */ + if (retry) + goto again; + } - if (alist.index > 0) - fprobe_set_ips(alist.addrs, alist.index, 1, 0); mutex_unlock(&fprobe_mutex); kfree(alist.addrs); From 1aed73795392d64193a4d8f30bbe355bca7d92fd Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 21 May 2026 08:58:13 -0400 Subject: [PATCH 074/697] tracing/fprobe: Check the same type fprobe on table as the unregistered one [ Upstream commit 0ac0058a74ac5765c7ce09ea630f4fdeaf4d80fa ] Commit 2c67dc457bc6 ("tracing: fprobe: optimization for entry only case") introduced a different ftrace_ops for entry-only fprobes. However, when unregistering an fprobe, the kernel only checks if another fprobe exists at the same address, without checking which type of fprobe it is. If different fprobes are registered at the same address, the same address will be registered in both fgraph_ops and ftrace_ops, but only one of them will be deleted when unregistering. (the one removed first will not be deleted from the ops). This results in junk entries remaining in either fgraph_ops or ftrace_ops. For example: ======= cd /sys/kernel/tracing # 'Add entry and exit events on the same place' echo 'f:event1 vfs_read' >> dynamic_events echo 'f:event2 vfs_read%return' >> dynamic_events # 'Enable both of them' echo 1 > events/fprobes/enable cat enabled_functions vfs_read (2) ->arch_ftrace_ops_list_func+0x0/0x210 # 'Disable and remove exit event' echo 0 > events/fprobes/event2/enable echo -:event2 >> dynamic_events # 'Disable and remove all events' echo 0 > events/fprobes/enable echo > dynamic_events # 'Add another event' echo 'f:event3 vfs_open%return' > dynamic_events cat dynamic_events f:fprobes/event3 vfs_open%return echo 1 > events/fprobes/enable cat enabled_functions vfs_open (1) tramp: 0xffffffffa0001000 (ftrace_graph_func+0x0/0x60) ->ftrace_graph_func+0x0/0x60 subops: {ent:fprobe_fgraph_entry+0x0/0x620 ret:fprobe_return+0x0/0x150} vfs_read (1) tramp: 0xffffffffa0001000 (ftrace_graph_func+0x0/0x60) ->ftrace_graph_func+0x0/0x60 subops: {ent:fprobe_fgraph_entry+0x0/0x620 ret:fprobe_return+0x0/0x150} ======= As you can see, an entry for the vfs_read remains. To fix this issue, when unregistering, the kernel should also check if there is the same type of fprobes still exist at the same address, and if not, delete its entry from either fgraph_ops or ftrace_ops. Link: https://lore.kernel.org/all/177669367993.132053.10553046138528674802.stgit@mhiramat.tok.corp.google.com/ Fixes: 2c67dc457bc6 ("tracing: fprobe: optimization for entry only case") Cc: stable@vger.kernel.org Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/trace/fprobe.c | 82 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 37a4352efb4d..6419c8d77273 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -92,11 +92,8 @@ static int insert_fprobe_node(struct fprobe_hlist_node *node, struct fprobe *fp) return ret; } -/* Return true if there are synonims */ -static bool delete_fprobe_node(struct fprobe_hlist_node *node) +static void delete_fprobe_node(struct fprobe_hlist_node *node) { - bool ret; - lockdep_assert_held(&fprobe_mutex); /* Avoid double deleting and non-inserted nodes */ @@ -105,13 +102,6 @@ static bool delete_fprobe_node(struct fprobe_hlist_node *node) rhltable_remove(&fprobe_ip_table, &node->hlist, fprobe_rht_params); } - - rcu_read_lock(); - ret = !!rhltable_lookup(&fprobe_ip_table, &node->addr, - fprobe_rht_params); - rcu_read_unlock(); - - return ret; } /* Check existence of the fprobe */ @@ -343,6 +333,32 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return !fp->exit_handler; } +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head = rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We have to check the same type on the list. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr != ip) + break; + fp = READ_ONCE(node->fp); + if (likely(fp)) { + if ((!ftrace && fp->exit_handler) || + (ftrace && !fp->exit_handler)) + return true; + } + } + + return false; +} + #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { @@ -365,6 +381,29 @@ static bool fprobe_is_ftrace(struct fprobe *fp) return false; } +static bool fprobe_exists_on_hash(unsigned long ip, bool ftrace __maybe_unused) +{ + struct rhlist_head *head, *pos; + struct fprobe_hlist_node *node; + struct fprobe *fp; + + guard(rcu)(); + head = rhltable_lookup(&fprobe_ip_table, &ip, + fprobe_rht_params); + if (!head) + return false; + /* We only need to check fp is there. */ + rhl_for_each_entry_rcu(node, pos, head, hlist) { + if (node->addr != ip) + break; + fp = READ_ONCE(node->fp); + if (likely(fp)) + return true; + } + + return false; +} + #ifdef CONFIG_MODULES static void fprobe_remove_ips(unsigned long *ips, unsigned int cnt) { @@ -552,18 +591,25 @@ struct fprobe_addr_list { static int fprobe_remove_node_in_module(struct module *mod, struct fprobe_hlist_node *node, struct fprobe_addr_list *alist) { + lockdep_assert_in_rcu_read_lock(); + if (!within_module(node->addr, mod)) return 0; - if (delete_fprobe_node(node)) - return 0; + delete_fprobe_node(node); /* If no address list is available, we can't track this address. */ if (!alist->addrs) return 0; + /* + * Don't care the type here, because all fprobes on the same + * address must be removed eventually. + */ + if (!rhltable_lookup(&fprobe_ip_table, &node->addr, fprobe_rht_params)) { + alist->addrs[alist->index++] = node->addr; + if (alist->index == alist->size) + return -ENOSPC; + } - alist->addrs[alist->index++] = node->addr; - if (alist->index == alist->size) - return -ENOSPC; return 0; } @@ -934,7 +980,9 @@ static int unregister_fprobe_nolock(struct fprobe *fp) /* Remove non-synonim ips from table and hash */ count = 0; for (i = 0; i < hlist_array->size; i++) { - if (!delete_fprobe_node(&hlist_array->array[i]) && addrs) + delete_fprobe_node(&hlist_array->array[i]); + if (addrs && !fprobe_exists_on_hash(hlist_array->array[i].addr, + fprobe_is_ftrace(fp))) addrs[count++] = hlist_array->array[i].addr; } del_fprobe_hash(fp); From 752ea4a105e6f42cabbb70e335baab3b5b2ddafd Mon Sep 17 00:00:00 2001 From: Guopeng Zhang Date: Fri, 22 May 2026 08:54:20 -0400 Subject: [PATCH 075/697] cgroup/cpuset: Reset DL migration state on can_attach() failure [ Upstream commit 4a39eda5fdd867fc39f3c039714dd432cee00268 ] cpuset_can_attach() accumulates temporary SCHED_DEADLINE migration state in the destination cpuset while walking the taskset. If a later task_can_attach() or security_task_setscheduler() check fails, cgroup_migrate_execute() treats cpuset as the failing subsystem and does not call cpuset_cancel_attach() for it. The partially accumulated state is then left behind and can be consumed by a later attach, corrupting cpuset DL task accounting and pending DL bandwidth accounting. Reset the pending DL migration state from the common error exit when ret is non-zero. Successful can_attach() keeps the state for cpuset_attach() or cpuset_cancel_attach(). Fixes: 2ef269ef1ac0 ("cgroup/cpuset: Free DL BW in case can_attach() fails") Cc: stable@vger.kernel.org # v6.10+ Signed-off-by: Guopeng Zhang Signed-off-by: Tejun Heo Reviewed-by: Chen Ridong Reviewed-by: Waiman Long [ omitted upstream context line `cs->dl_bw_cpu = cpu;` ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- kernel/cgroup/cpuset.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 08b0c264bd26..5057cf44342b 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -3163,16 +3163,13 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) int cpu = cpumask_any_and(cpu_active_mask, cs->effective_cpus); if (unlikely(cpu >= nr_cpu_ids)) { - reset_migrate_dl_data(cs); ret = -EINVAL; goto out_unlock; } ret = dl_bw_alloc(cpu, cs->sum_migrate_dl_bw); - if (ret) { - reset_migrate_dl_data(cs); + if (ret) goto out_unlock; - } } out_success: @@ -3181,7 +3178,10 @@ static int cpuset_can_attach(struct cgroup_taskset *tset) * changes which zero cpus/mems_allowed. */ cs->attach_in_progress++; + out_unlock: + if (ret) + reset_migrate_dl_data(cs); mutex_unlock(&cpuset_mutex); return ret; } From 61f53c1e58d68723bc1db10912a53f1991f08719 Mon Sep 17 00:00:00 2001 From: Quan Sun <2022090917019@std.uestc.edu.cn> Date: Fri, 22 May 2026 09:02:59 -0400 Subject: [PATCH 076/697] net: ethtool: fix NULL pointer dereference in phy_reply_size [ Upstream commit 4908f1395fb1b832ceec11584af649874a2732ea ] In phy_prepare_data(), several strings such as 'name', 'drvname', 'upstream_sfp_name', and 'downstream_sfp_name' are allocated using kstrdup(). However, these allocations were not checked for failure. If kstrdup() fails for 'name', it returns NULL while the function continues. This leads to a kernel NULL pointer dereference and panic later in phy_reply_size() when it unconditionally calls strlen() on the NULL pointer. While other strings like 'upstream_sfp_name' might be checked before access in certain code paths, failing to handle these allocations consistently can lead to incomplete data reporting or hidden bugs. Fix this by adding proper NULL checks for all kstrdup() calls in phy_prepare_data() and implement a centralized error handling path using goto labels to ensure all previously allocated resources are freed on failure. Fixes: 9dd2ad5e92b9 ("net: ethtool: phy: Convert the PHY_GET command to generic phy dump") Signed-off-by: Quan Sun <2022090917019@std.uestc.edu.cn> Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260507131738.1173835-1-2022090917019@std.uestc.edu.cn Signed-off-by: Jakub Kicinski Stable-dep-of: e3adf69f8eb1 ("net: ethtool: phy: avoid NULL deref when PHY driver is unbound") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ethtool/phy.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c index 68372bef4b2f..cb1e0aea450f 100644 --- a/net/ethtool/phy.c +++ b/net/ethtool/phy.c @@ -76,6 +76,7 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, struct nlattr **tb = info->attrs; struct phy_device_node *pdn; struct phy_device *phydev; + int ret; /* RTNL is held by the caller */ phydev = ethnl_req_get_phydev(req_info, tb, ETHTOOL_A_PHY_HEADER, @@ -88,8 +89,17 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, return -EOPNOTSUPP; rep_data->phyindex = phydev->phyindex; + rep_data->name = kstrdup(dev_name(&phydev->mdio.dev), GFP_KERNEL); + if (!rep_data->name) + return -ENOMEM; + rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); + if (!rep_data->drvname) { + ret = -ENOMEM; + goto err_free_name; + } + rep_data->upstream_type = pdn->upstream_type; if (pdn->upstream_type == PHY_UPSTREAM_PHY) { @@ -97,15 +107,33 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, rep_data->upstream_index = upstream->phyindex; } - if (pdn->parent_sfp_bus) + if (pdn->parent_sfp_bus) { rep_data->upstream_sfp_name = kstrdup(sfp_get_name(pdn->parent_sfp_bus), GFP_KERNEL); + if (!rep_data->upstream_sfp_name) { + ret = -ENOMEM; + goto err_free_drvname; + } + } - if (phydev->sfp_bus) + if (phydev->sfp_bus) { rep_data->downstream_sfp_name = kstrdup(sfp_get_name(phydev->sfp_bus), GFP_KERNEL); + if (!rep_data->downstream_sfp_name) { + ret = -ENOMEM; + goto err_free_upstream_sfp; + } + } return 0; + +err_free_upstream_sfp: + kfree(rep_data->upstream_sfp_name); +err_free_drvname: + kfree(rep_data->drvname); +err_free_name: + kfree(rep_data->name); + return ret; } static int phy_fill_reply(struct sk_buff *skb, From 3586924625559e6f9876d726c80ff0a75f0d5849 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 22 May 2026 09:03:00 -0400 Subject: [PATCH 077/697] net: ethtool: phy: avoid NULL deref when PHY driver is unbound [ Upstream commit e3adf69f8eb121a9128c2b0029efd050d3649153 ] phydev->drv can become NULL while the phy_device is still attached to its net_device, namely after the PHY driver is unbound via sysfs: echo > /sys/bus/mdio_bus/drivers//unbind phy_remove() clears phydev->drv but doesn't call phy_detach(), so the phy_device stays in the link topology xarray and ethnl_req_get_phydev() still hands it back. ETHTOOL_MSG_PHY_GET then oopses on: rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); drvname is already treated as optional by phy_reply_size(), phy_fill_reply() and phy_cleanup_data(), so just skip the allocation when there is no driver bound. Fixes: 9dd2ad5e92b9 ("net: ethtool: phy: Convert the PHY_GET command to generic phy dump") Cc: stable@vger.kernel.org # 6.13.x Signed-off-by: David Carlier Reviewed-by: Maxime Chevallier Tested-by: Maxime Chevallier Link: https://patch.msgid.link/20260509215046.107157-1-devnexen@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/ethtool/phy.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/ethtool/phy.c b/net/ethtool/phy.c index cb1e0aea450f..98392a3c34b5 100644 --- a/net/ethtool/phy.c +++ b/net/ethtool/phy.c @@ -94,10 +94,12 @@ static int phy_prepare_data(const struct ethnl_req_info *req_info, if (!rep_data->name) return -ENOMEM; - rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); - if (!rep_data->drvname) { - ret = -ENOMEM; - goto err_free_name; + if (phydev->drv) { + rep_data->drvname = kstrdup(phydev->drv->name, GFP_KERNEL); + if (!rep_data->drvname) { + ret = -ENOMEM; + goto err_free_name; + } } rep_data->upstream_type = pdn->upstream_type; From d73dcd1520d65a34420761641a36b951b14c8c53 Mon Sep 17 00:00:00 2001 From: Konstantin Komarov Date: Thu, 28 May 2026 11:20:28 +0800 Subject: [PATCH 078/697] fs/ntfs3: handle attr_set_size() errors when truncating files [ Upstream commit 576248a34b927e93b2fd3fff7df735ba73ad7d01 ] If attr_set_size() fails while truncating down, the error is silently ignored and the inode may be left in an inconsistent state. Signed-off-by: Konstantin Komarov [ Minor context conflict resolved. ] Signed-off-by: Bin Lan Signed-off-by: Greg Kroah-Hartman --- fs/ntfs3/file.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index cd7aaeef45fe..5042eb321eb8 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -458,8 +458,8 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) { struct super_block *sb = inode->i_sb; struct ntfs_inode *ni = ntfs_i(inode); - int err, dirty = 0; u64 new_valid; + int err; if (!S_ISREG(inode->i_mode)) return 0; @@ -475,7 +475,6 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) } new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size)); - truncate_setsize(inode, new_size); ni_lock(ni); @@ -489,22 +488,19 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size) ni->i_valid = new_valid; ni_unlock(ni); + if (unlikely(err)) + return err; ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE; inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); if (!IS_DIRSYNC(inode)) { - dirty = 1; + mark_inode_dirty(inode); } else { err = ntfs_sync_inode(inode); if (err) return err; } - if (dirty) - mark_inode_dirty(inode); - - /*ntfs_flush_inodes(inode->i_sb, inode, NULL);*/ - return 0; } From e0c3dd7b30cc5ee42ab502da140cda93d794a20b Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 18 May 2026 14:34:47 -0400 Subject: [PATCH 079/697] l2tp: use list_del_rcu in l2tp_session_unhash commit 979c017803c40829b03acd9e5236e354b7622360 upstream. An unprivileged local user can pin a host CPU indefinitely in l2tp_session_get_by_ifname() by issuing L2TP_CMD_SESSION_GET on L2TP_ATTR_IFNAME concurrently with L2TP_CMD_SESSION_CREATE and L2TP_CMD_SESSION_DELETE on the same tunnel. All three commands take GENL_UNS_ADMIN_PERM, so CAP_NET_ADMIN in the netns user namespace suffices; on any host that has l2tp_core loaded the trigger is reachable from a standard `unshare -Urn` sandbox. l2tp_session_unhash() removes a session from tunnel->session_list with list_del_init(), but that list is walked by l2tp_session_get_by_ifname() with list_for_each_entry_rcu() under rcu_read_lock_bh(). list_del_init() leaves the deleted entry's next/prev self-pointing; a reader that has loaded the entry and then advances pos->list.next reads &session->list, container_of()s back to the same session, and list_for_each_entry_rcu() never reaches the list head. The CPU stays in strcmp() inside the walker, with BH and preemption disabled, so RCU grace periods on the host stall behind it and the wedged thread cannot be killed (SIGKILL is delivered on syscall return). Use list_del_rcu() to match the existing list_add_rcu() in l2tp_session_register(); the deleted session remains visible to in-flight walkers with consistent next/prev pointers until kfree_rcu() in l2tp_session_free() releases it. tunnel->session_list has exactly one list_del_init() call site; the list_del_init (&session->clist) at l2tp_core.c:533 operates on the per-collision list, which is not walked under RCU. list_empty(&session->list) is not used anywhere in net/l2tp/ after the unhash point, so dropping the post-delete self-init is safe; the fix has no userspace-visible behavior change. Fixes: 89b768ec2dfef ("l2tp: use rcu list add/del when updating lists") Cc: stable@vger.kernel.org # 6.11+ Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260518183447.64078-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 9156a937334a..be32bf45d583 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1360,7 +1360,7 @@ static void l2tp_session_unhash(struct l2tp_session *session) spin_lock_bh(&pn->l2tp_session_idr_lock); /* Remove from the per-tunnel list */ - list_del_init(&session->list); + list_del_rcu(&session->list); /* Remove from per-net IDR */ if (tunnel->version == L2TP_HDR_VER_3) { From 0e47fc1c9181ae029e0e35a865cbf2adcbae626c Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 20 May 2026 15:03:23 +0800 Subject: [PATCH 080/697] qed: fix double free in qed_cxt_tables_alloc() commit 2bccfb8476ca5f3548afbd623dc7a6980d4e77de upstream. If one of the later PF or VF CID bitmap allocations fails, qed_cid_map_alloc() jumps to cid_map_fail and frees the previously allocated CID bitmaps before returning an error. qed_cxt_tables_alloc() then calls qed_cxt_mngr_free(), which invokes qed_cid_map_free() again. Fix this by setting each CID bitmap pointer to NULL after bitmap_free() to avoid double free. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc3. Runtime reproduction was not attempted because exercising the failing allocation path requires device-specific setup. Fixes: fe56b9e6a8d9 ("qed: Add module with basic common support") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Link: https://patch.msgid.link/20260520070323.2762379-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/qlogic/qed/qed_cxt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/qlogic/qed/qed_cxt.c b/drivers/net/ethernet/qlogic/qed/qed_cxt.c index 33f4f58ee51c..1fb09372c25a 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c +++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c @@ -1038,11 +1038,13 @@ static void qed_cid_map_free(struct qed_hwfn *p_hwfn) for (type = 0; type < MAX_CONN_TYPES; type++) { bitmap_free(p_mngr->acquired[type].cid_map); + p_mngr->acquired[type].cid_map = NULL; p_mngr->acquired[type].max_count = 0; p_mngr->acquired[type].start_cid = 0; for (vf = 0; vf < MAX_NUM_VFS; vf++) { bitmap_free(p_mngr->acquired_vf[type][vf].cid_map); + p_mngr->acquired_vf[type][vf].cid_map = NULL; p_mngr->acquired_vf[type][vf].max_count = 0; p_mngr->acquired_vf[type][vf].start_cid = 0; } From 610ff6bc2f44b8beb70ab7913e1da305a1ebfa13 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Wed, 20 May 2026 22:08:01 -0400 Subject: [PATCH 081/697] ring-buffer: Fix reporting of missed events in iterator commit a254b6d13b0edd6272926674d2afc46d46e496b7 upstream. When tracing is active while reading the trace file, if the iterator reading the buffer detects that the writer has passed the iterator head, it will reset and set a "missed events" flag. This flag is passed to the output processing to show the user that events were missed: CPU:4 [LOST EVENTS] The problem is that the flag is reset after it is checked in ring_buffer_iter_dropped(). But the "trace" file iterates over all the CPU ring buffers and it will check if they are dropped when figuring out which buffer to print next. This prematurely clears the missed_events flag if the CPU buffer with the missed events is not the one that is printed next. On the iteration where the CPU buffer with the missed events is printed, the check if it had missed events would return false and the output does not show that events were missed. Do not reset the missed_events flag when checking if there were missed events, but instead clear it when moving the iterator head to the next event. Cc: stable@vger.kernel.org Cc: Mathieu Desnoyers Link: https://patch.msgid.link/20260520220801.4fd09d13@fedora Fixes: c9b7a4a72ff64 ("ring-buffer/tracing: Have iterator acknowledge dropped events") Acked-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index a31fb4b7a52e..fdce9101c0b1 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5249,6 +5249,7 @@ static void rb_iter_reset(struct ring_buffer_iter *iter) iter->head_page = cpu_buffer->reader_page; iter->head = cpu_buffer->reader_page->read; iter->next_event = iter->head; + iter->missed_events = 0; iter->cache_reader_page = iter->head_page; iter->cache_read = cpu_buffer->read; @@ -5863,10 +5864,7 @@ ring_buffer_peek(struct trace_buffer *buffer, int cpu, u64 *ts, */ bool ring_buffer_iter_dropped(struct ring_buffer_iter *iter) { - bool ret = iter->missed_events != 0; - - iter->missed_events = 0; - return ret; + return iter->missed_events != 0; } EXPORT_SYMBOL_GPL(ring_buffer_iter_dropped); @@ -6028,7 +6026,7 @@ void ring_buffer_iter_advance(struct ring_buffer_iter *iter) unsigned long flags; raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); - + iter->missed_events = 0; rb_advance_iter(iter); raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); From 2bc60c175568ecf0e8b7803dcbf54bd61f3cf76e Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 30 Apr 2026 12:28:16 +0900 Subject: [PATCH 082/697] ring-buffer: Flush and stop persistent ring buffer on panic commit a494d3c8d5392bcdff83c2a593df0c160ff9f322 upstream. On real hardware, panic and machine reboot may not flush hardware cache to memory. This means the persistent ring buffer, which relies on a coherent state of memory, may not have its events written to the buffer and they may be lost. Moreover, there may be inconsistency with the counters which are used for validation of the integrity of the persistent ring buffer which may cause all data to be discarded. To avoid this issue, stop recording of the ring buffer on panic and flush the cache of the ring buffer's memory. Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance") Cc: stable@vger.kernel.org Cc: Will Deacon Cc: Mathieu Desnoyers Cc: Ian Rogers Link: https://patch.msgid.link/177751969602.2136606.12031934362587643488.stgit@mhiramat.tok.corp.google.com Signed-off-by: Masami Hiramatsu (Google) Acked-by: Catalin Marinas Acked-by: Geert Uytterhoeven Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/Kbuild | 1 + arch/arc/include/asm/Kbuild | 1 + arch/arm/include/asm/Kbuild | 1 + arch/arm64/include/asm/ring_buffer.h | 10 ++++++++++ arch/csky/include/asm/Kbuild | 1 + arch/hexagon/include/asm/Kbuild | 1 + arch/loongarch/include/asm/Kbuild | 1 + arch/m68k/include/asm/Kbuild | 1 + arch/microblaze/include/asm/Kbuild | 1 + arch/mips/include/asm/Kbuild | 1 + arch/nios2/include/asm/Kbuild | 1 + arch/openrisc/include/asm/Kbuild | 1 + arch/parisc/include/asm/Kbuild | 1 + arch/powerpc/include/asm/Kbuild | 1 + arch/riscv/include/asm/Kbuild | 1 + arch/s390/include/asm/Kbuild | 1 + arch/sh/include/asm/Kbuild | 1 + arch/sparc/include/asm/Kbuild | 1 + arch/um/include/asm/Kbuild | 1 + arch/x86/include/asm/Kbuild | 1 + arch/xtensa/include/asm/Kbuild | 1 + include/asm-generic/ring_buffer.h | 13 +++++++++++++ kernel/trace/ring_buffer.c | 22 ++++++++++++++++++++++ 23 files changed, 65 insertions(+) create mode 100644 arch/arm64/include/asm/ring_buffer.h create mode 100644 include/asm-generic/ring_buffer.h diff --git a/arch/alpha/include/asm/Kbuild b/arch/alpha/include/asm/Kbuild index 483965c5a4de..b154b4e3dfa8 100644 --- a/arch/alpha/include/asm/Kbuild +++ b/arch/alpha/include/asm/Kbuild @@ -5,4 +5,5 @@ generic-y += agp.h generic-y += asm-offsets.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += text-patching.h diff --git a/arch/arc/include/asm/Kbuild b/arch/arc/include/asm/Kbuild index 4c69522e0328..483caacc6988 100644 --- a/arch/arc/include/asm/Kbuild +++ b/arch/arc/include/asm/Kbuild @@ -5,5 +5,6 @@ generic-y += extable.h generic-y += kvm_para.h generic-y += mcs_spinlock.h generic-y += parport.h +generic-y += ring_buffer.h generic-y += user.h generic-y += text-patching.h diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild index 03657ff8fbe3..decad5f2c826 100644 --- a/arch/arm/include/asm/Kbuild +++ b/arch/arm/include/asm/Kbuild @@ -3,6 +3,7 @@ generic-y += early_ioremap.h generic-y += extable.h generic-y += flat.h generic-y += parport.h +generic-y += ring_buffer.h generated-y += mach-types.h generated-y += unistd-nr.h diff --git a/arch/arm64/include/asm/ring_buffer.h b/arch/arm64/include/asm/ring_buffer.h new file mode 100644 index 000000000000..62316c406888 --- /dev/null +++ b/arch/arm64/include/asm/ring_buffer.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_ARM64_RING_BUFFER_H +#define _ASM_ARM64_RING_BUFFER_H + +#include + +/* Flush D-cache on persistent ring buffer */ +#define arch_ring_buffer_flush_range(start, end) dcache_clean_pop(start, end) + +#endif /* _ASM_ARM64_RING_BUFFER_H */ diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild index 3a5c7f6e5aac..7dca0c6cdc84 100644 --- a/arch/csky/include/asm/Kbuild +++ b/arch/csky/include/asm/Kbuild @@ -9,6 +9,7 @@ generic-y += qrwlock.h generic-y += qrwlock_types.h generic-y += qspinlock.h generic-y += parport.h +generic-y += ring_buffer.h generic-y += user.h generic-y += vmlinux.lds.h generic-y += text-patching.h diff --git a/arch/hexagon/include/asm/Kbuild b/arch/hexagon/include/asm/Kbuild index 1efa1e993d4b..0f887d4238ed 100644 --- a/arch/hexagon/include/asm/Kbuild +++ b/arch/hexagon/include/asm/Kbuild @@ -5,4 +5,5 @@ generic-y += extable.h generic-y += iomap.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += text-patching.h diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild index b04d2cef935f..bdd9f0aa8f71 100644 --- a/arch/loongarch/include/asm/Kbuild +++ b/arch/loongarch/include/asm/Kbuild @@ -9,5 +9,6 @@ generic-y += qrwlock.h generic-y += user.h generic-y += ioctl.h generic-y += mmzone.h +generic-y += ring_buffer.h generic-y += statfs.h generic-y += text-patching.h diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild index b282e0dd8dc1..62543bf305ff 100644 --- a/arch/m68k/include/asm/Kbuild +++ b/arch/m68k/include/asm/Kbuild @@ -3,5 +3,6 @@ generated-y += syscall_table.h generic-y += extable.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += spinlock.h generic-y += text-patching.h diff --git a/arch/microblaze/include/asm/Kbuild b/arch/microblaze/include/asm/Kbuild index 7178f990e8b3..0030309b47ad 100644 --- a/arch/microblaze/include/asm/Kbuild +++ b/arch/microblaze/include/asm/Kbuild @@ -5,6 +5,7 @@ generic-y += extable.h generic-y += kvm_para.h generic-y += mcs_spinlock.h generic-y += parport.h +generic-y += ring_buffer.h generic-y += syscalls.h generic-y += tlb.h generic-y += user.h diff --git a/arch/mips/include/asm/Kbuild b/arch/mips/include/asm/Kbuild index 684569b2ecd6..9771c3d85074 100644 --- a/arch/mips/include/asm/Kbuild +++ b/arch/mips/include/asm/Kbuild @@ -12,5 +12,6 @@ generic-y += mcs_spinlock.h generic-y += parport.h generic-y += qrwlock.h generic-y += qspinlock.h +generic-y += ring_buffer.h generic-y += user.h generic-y += text-patching.h diff --git a/arch/nios2/include/asm/Kbuild b/arch/nios2/include/asm/Kbuild index 28004301c236..0a2530964413 100644 --- a/arch/nios2/include/asm/Kbuild +++ b/arch/nios2/include/asm/Kbuild @@ -5,6 +5,7 @@ generic-y += cmpxchg.h generic-y += extable.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += spinlock.h generic-y += user.h generic-y += text-patching.h diff --git a/arch/openrisc/include/asm/Kbuild b/arch/openrisc/include/asm/Kbuild index cef49d60d74c..8aa34621702d 100644 --- a/arch/openrisc/include/asm/Kbuild +++ b/arch/openrisc/include/asm/Kbuild @@ -8,4 +8,5 @@ generic-y += spinlock_types.h generic-y += spinlock.h generic-y += qrwlock_types.h generic-y += qrwlock.h +generic-y += ring_buffer.h generic-y += user.h diff --git a/arch/parisc/include/asm/Kbuild b/arch/parisc/include/asm/Kbuild index 4fb596d94c89..d48d158f7241 100644 --- a/arch/parisc/include/asm/Kbuild +++ b/arch/parisc/include/asm/Kbuild @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h generic-y += agp.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += user.h diff --git a/arch/powerpc/include/asm/Kbuild b/arch/powerpc/include/asm/Kbuild index 2e23533b67e3..805b5aeebb6f 100644 --- a/arch/powerpc/include/asm/Kbuild +++ b/arch/powerpc/include/asm/Kbuild @@ -5,4 +5,5 @@ generated-y += syscall_table_spu.h generic-y += agp.h generic-y += mcs_spinlock.h generic-y += qrwlock.h +generic-y += ring_buffer.h generic-y += early_ioremap.h diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index bd5fc9403295..7721b63642f4 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -14,5 +14,6 @@ generic-y += ticket_spinlock.h generic-y += qrwlock.h generic-y += qrwlock_types.h generic-y += qspinlock.h +generic-y += ring_buffer.h generic-y += user.h generic-y += vmlinux.lds.h diff --git a/arch/s390/include/asm/Kbuild b/arch/s390/include/asm/Kbuild index 297bf7157968..2b367fa4de8e 100644 --- a/arch/s390/include/asm/Kbuild +++ b/arch/s390/include/asm/Kbuild @@ -8,3 +8,4 @@ generic-y += asm-offsets.h generic-y += kvm_types.h generic-y += mcs_spinlock.h generic-y += mmzone.h +generic-y += ring_buffer.h diff --git a/arch/sh/include/asm/Kbuild b/arch/sh/include/asm/Kbuild index 4d3f10ed8275..f0403d3ee8ab 100644 --- a/arch/sh/include/asm/Kbuild +++ b/arch/sh/include/asm/Kbuild @@ -3,4 +3,5 @@ generated-y += syscall_table.h generic-y += kvm_para.h generic-y += mcs_spinlock.h generic-y += parport.h +generic-y += ring_buffer.h generic-y += text-patching.h diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index 17ee8a273aa6..49c6bb326b75 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -4,4 +4,5 @@ generated-y += syscall_table_64.h generic-y += agp.h generic-y += kvm_para.h generic-y += mcs_spinlock.h +generic-y += ring_buffer.h generic-y += text-patching.h diff --git a/arch/um/include/asm/Kbuild b/arch/um/include/asm/Kbuild index b6810db24ca4..9be3ee2e3701 100644 --- a/arch/um/include/asm/Kbuild +++ b/arch/um/include/asm/Kbuild @@ -18,6 +18,7 @@ generic-y += module.lds.h generic-y += parport.h generic-y += percpu.h generic-y += preempt.h +generic-y += ring_buffer.h generic-y += runtime-const.h generic-y += softirq_stack.h generic-y += switch_to.h diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index 4566000e15c4..078fd2c0d69d 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -14,3 +14,4 @@ generic-y += early_ioremap.h generic-y += fprobe.h generic-y += mcs_spinlock.h generic-y += mmzone.h +generic-y += ring_buffer.h diff --git a/arch/xtensa/include/asm/Kbuild b/arch/xtensa/include/asm/Kbuild index 13fe45dea296..e57af619263a 100644 --- a/arch/xtensa/include/asm/Kbuild +++ b/arch/xtensa/include/asm/Kbuild @@ -6,5 +6,6 @@ generic-y += mcs_spinlock.h generic-y += parport.h generic-y += qrwlock.h generic-y += qspinlock.h +generic-y += ring_buffer.h generic-y += user.h generic-y += text-patching.h diff --git a/include/asm-generic/ring_buffer.h b/include/asm-generic/ring_buffer.h new file mode 100644 index 000000000000..201d2aee1005 --- /dev/null +++ b/include/asm-generic/ring_buffer.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Generic arch dependent ring_buffer macros. + */ +#ifndef __ASM_GENERIC_RING_BUFFER_H__ +#define __ASM_GENERIC_RING_BUFFER_H__ + +#include + +/* Flush cache on ring buffer range if needed. Do nothing by default. */ +#define arch_ring_buffer_flush_range(start, end) do { } while (0) + +#endif /* __ASM_GENERIC_RING_BUFFER_H__ */ diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index fdce9101c0b1..bef1c05b9b71 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5,6 +5,7 @@ * Copyright (C) 2008 Steven Rostedt */ #include +#include #include #include #include @@ -29,6 +30,7 @@ #include #include +#include #include #include #include @@ -553,6 +555,7 @@ struct trace_buffer { unsigned long range_addr_start; unsigned long range_addr_end; + struct notifier_block flush_nb; struct ring_buffer_meta *meta; @@ -2455,6 +2458,16 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) kfree(cpu_buffer); } +/* Stop recording on a persistent buffer and flush cache if needed. */ +static int rb_flush_buffer_cb(struct notifier_block *nb, unsigned long event, void *data) +{ + struct trace_buffer *buffer = container_of(nb, struct trace_buffer, flush_nb); + + ring_buffer_record_off(buffer); + arch_ring_buffer_flush_range(buffer->range_addr_start, buffer->range_addr_end); + return NOTIFY_DONE; +} + static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, int order, unsigned long start, unsigned long end, @@ -2574,6 +2587,12 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, mutex_init(&buffer->mutex); + /* Persistent ring buffer needs to flush cache before reboot. */ + if (start && end) { + buffer->flush_nb.notifier_call = rb_flush_buffer_cb; + atomic_notifier_chain_register(&panic_notifier_list, &buffer->flush_nb); + } + return_ptr(buffer); fail_free_buffers: @@ -2661,6 +2680,9 @@ ring_buffer_free(struct trace_buffer *buffer) { int cpu; + if (buffer->range_addr_start && buffer->range_addr_end) + atomic_notifier_chain_unregister(&panic_notifier_list, &buffer->flush_nb); + cpuhp_state_remove_instance(CPUHP_TRACE_RB_PREPARE, &buffer->node); irq_work_sync(&buffer->irq_work.work); From abdd03229414b5a52943b65a60f34b84cea5ac59 Mon Sep 17 00:00:00 2001 From: Justin Iurman Date: Sun, 17 May 2026 20:30:59 +0200 Subject: [PATCH 083/697] ipv6: ioam: add NULL check for idev in ipv6_hop_ioam() commit d4ea0dfd75011b78cebf3808f98ac4c4f51a6fb9 upstream. Reported by Sashiko: The function ipv6_hop_ioam() accesses __in6_dev_get(skb->dev)->cnf.ioam6_enabled without validating the returned idev pointer. Because addrconf_ifdown() can concurrently clear dev->ip6_ptr via RCU, __in6_dev_get() can return NULL during interface teardown, which could cause a NULL pointer dereference when processing an IOAM Hop-by-Hop option. Let's add a check and use SKB_DROP_REASON_IPV6DISABLED accordingly. Fixes: 9ee11f0fff20 ("ipv6: ioam: Data plane support for Pre-allocated Trace") Cc: stable@vger.kernel.org Signed-off-by: Justin Iurman Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260517183059.29140-1-justin.iurman@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/exthdrs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 9b4ea568b8d7..d15e60943820 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -910,16 +910,27 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) { + enum skb_drop_reason drop_reason; struct ioam6_trace_hdr *trace; struct ioam6_namespace *ns; + struct inet6_dev *idev; struct ioam6_hdr *hdr; + drop_reason = SKB_DROP_REASON_IP_INHDR; + /* Bad alignment (must be 4n-aligned) */ if (optoff & 3) goto drop; + /* Does the device still have IPv6 configuration? */ + idev = __in6_dev_get(skb->dev); + if (!idev) { + drop_reason = SKB_DROP_REASON_IPV6DISABLED; + goto drop; + } + /* Ignore if IOAM is not enabled on ingress */ - if (!READ_ONCE(__in6_dev_get(skb->dev)->cnf.ioam6_enabled)) + if (!READ_ONCE(idev->cnf.ioam6_enabled)) goto ignore; /* Truncated Option header */ @@ -972,7 +983,7 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) return true; drop: - kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR); + kfree_skb_reason(skb, drop_reason); return false; } From 29b6433510127e19f67378f92af6c0cb2ec1c4d4 Mon Sep 17 00:00:00 2001 From: Li Xiasong Date: Fri, 15 May 2026 06:27:33 +0200 Subject: [PATCH 084/697] mptcp: pm: fix ADD_ADDR timer infinite retry on option space insufficient commit 51e398a3b8961b26a8c0a4ba9a777c5339791707 upstream. When TCP option space is insufficient (e.g., when sending ADD_ADDR with an IPv6 address and port while tcp_timestamps is enabled), the original code jumped to out_unlock without clearing the addr_signal flag. This caused mptcp_pm_add_timer to keep rescheduling indefinitely, not sending ADD_ADDR, preventing subsequent addresses in the endpoint list from being announced. Handle this case by clearing the ADD_ADDR signal and skipping the matching ADD_ADDR retransmission entry. The skip path cancels the matching timer (with id check) and advances PM state progression, preserving forward progress to subsequent PM work. This cancellation is inherently best-effort. A concurrent add_timer callback may already be running and may acquire pm.lock before the cancel path updates entry state. In that case, one final ADD_ADDR transmit attempt can still be executed. Once the cancel path sets entry->retrans_times to ADD_ADDR_RETRANS_MAX, the callback-side retrans_times check suppresses further ADD_ADDR retransmissions. Note that when an ADD_ADDR is being prepared, a pure-ACK is queued. On the output side, it means that it is fine to skip non-pure-ACK packets, when drop_other_suboptions is set: a pure-ACK will be processed soon after. Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout") Cc: stable@vger.kernel.org Signed-off-by: Li Xiasong Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-2-701e96419f2f@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/mptcp/pm.c | 56 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 8523d3af3ca7..6c995cc38a00 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -364,7 +364,13 @@ static void mptcp_pm_add_timer(struct timer_list *timer) spin_lock_bh(&msk->pm.lock); - if (!mptcp_pm_should_add_signal_addr(msk)) { + /* The cancel path (mptcp_pm_del_add_timer()) can race with this + * callback. Once cancel updates retrans_times to MAX, suppress further + * retransmissions here. If this callback acquires pm.lock first, one + * final transmit attempt is still possible. + */ + if (entry->retrans_times < ADD_ADDR_RETRANS_MAX && + !mptcp_pm_should_add_signal_addr(msk)) { pr_debug("retransmit ADD_ADDR id=%d\n", entry->addr.id); mptcp_pm_announce_addr(msk, &entry->addr, false); mptcp_pm_add_addr_send_ack(msk); @@ -414,8 +420,12 @@ mptcp_pm_del_add_timer(struct mptcp_sock *msk, /* Note: entry might have been removed by another thread. * We hold rcu_read_lock() to ensure it is not freed under us. */ - if (stop_timer) - sk_stop_timer_sync(sk, &entry->add_timer); + if (stop_timer) { + if (check_id) + sk_stop_timer(sk, &entry->add_timer); + else + sk_stop_timer_sync(sk, &entry->add_timer); + } rcu_read_unlock(); return entry; @@ -880,6 +890,7 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, struct mptcp_addr_info *addr, bool *echo, bool *drop_other_suboptions) { + bool skip_add_addr = false; int ret = false; u8 add_addr; u8 family; @@ -901,24 +912,49 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, const struct sk_buff *skb, } *echo = mptcp_pm_should_add_signal_echo(msk); - port = !!(*echo ? msk->pm.remote.port : msk->pm.local.port); - - family = *echo ? msk->pm.remote.family : msk->pm.local.family; - if (remaining < mptcp_add_addr_len(family, *echo, port)) - goto out_unlock; - if (*echo) { *addr = msk->pm.remote; add_addr = msk->pm.addr_signal & ~BIT(MPTCP_ADD_ADDR_ECHO); + port = !!msk->pm.remote.port; + family = msk->pm.remote.family; } else { *addr = msk->pm.local; add_addr = msk->pm.addr_signal & ~BIT(MPTCP_ADD_ADDR_SIGNAL); + port = !!msk->pm.local.port; + family = msk->pm.local.family; } - WRITE_ONCE(msk->pm.addr_signal, add_addr); + + if (remaining < mptcp_add_addr_len(family, *echo, port)) { + struct net *net = sock_net((struct sock *)msk); + + if (!*drop_other_suboptions) + goto out_unlock; + + if (*echo) { + MPTCP_INC_STATS(net, MPTCP_MIB_ECHOADDTXDROP); + } else { + skip_add_addr = true; + MPTCP_INC_STATS(net, MPTCP_MIB_ADDADDRTXDROP); + } + goto drop_signal_mark; + } + ret = true; +drop_signal_mark: + WRITE_ONCE(msk->pm.addr_signal, add_addr); + out_unlock: spin_unlock_bh(&msk->pm.lock); + + /* On pure-ACK option-space exhaustion, stop retrying this ADD_ADDR: + * clear the signal bit, cancel the matching retransmission timer, and + * let the PM state machine progress. + */ + if (skip_add_addr) { + mptcp_pm_del_add_timer(msk, addr, true); + mptcp_pm_subflow_established(msk); + } return ret; } From 440447699c681e26ed58e9c309cad718270a18b4 Mon Sep 17 00:00:00 2001 From: Minh Nguyen Date: Tue, 19 May 2026 17:23:10 +0700 Subject: [PATCH 085/697] vsock/vmci: fix UAF when peer resets connection during handshake commit 99e22ddf4edb63dc8382bc028af928056d3450cf upstream. vmci_transport_recv_connecting_server() returned err = 0 for a peer RST in its default switch arm: err = pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST ? 0 : -EINVAL; That made vmci_transport_recv_listen() skip vsock_remove_pending(), leaving the pending socket on the listener's pending_links with sk_state = TCP_CLOSE while destroy: still dropped the explicit reference taken before schedule_delayed_work(). One second later vsock_pending_work() observed is_pending=true and performed full cleanup: vsock_remove_pending() then the two trailing sock_put(sk) calls -- the first reached refcount 0 and __sk_freed the socket, and the second wrote into the freed object: BUG: KASAN: slab-use-after-free in refcount_warn_saturate Write of size 4 at addr ffff88800b1cac80 by task kworker Workqueue: events vsock_pending_work Treat peer RST like any other unexpected packet type (err = -EINVAL). All destroy: arms now return err < 0, so vmci_transport_recv_listen() removes pending from pending_links synchronously and vsock_pending_work() takes the is_pending=false / !rejected branch, dropping only its own work reference. This also closes the multi-packet race Sashiko reported on v2: pending is removed from the list before any subsequent packet can find it. The pre-existing sk_acceptq_removed() gap on the err < 0 path of vmci_transport_recv_listen() that Sashiko also noted is not introduced or changed by this patch. Tested on lts-6.12.79 with KASAN: 52/100 unpatched -> 0/100 patched. Fixes: d021c344051a ("VSOCK: Introduce VM Sockets") Cc: stable@vger.kernel.org Signed-off-by: Minh Nguyen Acked-by: Bryan Tan Link: https://patch.msgid.link/20260519102310.237181-1-minhnguyen.080505@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/vmw_vsock/vmci_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index aca3132689cf..4cd11f355e9d 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -1156,7 +1156,7 @@ vmci_transport_recv_connecting_server(struct sock *listener, /* Close and cleanup the connection. */ vmci_transport_send_reset(pending, pkt); skerr = EPROTO; - err = pkt->type == VMCI_TRANSPORT_PACKET_TYPE_RST ? 0 : -EINVAL; + err = -EINVAL; goto destroy; } From c618cf8926c0574c32649c56331f6ecee674928e Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 18 May 2026 11:06:55 +0200 Subject: [PATCH 086/697] vsock/virtio: reset connection on receiving queue overflow commit a4f0b001782b21663d10df983b4b208195bec66c upstream. When there is no more space to queue an incoming packet, the packet is silently dropped. This causes data loss without any notification to either peer, since there is no retransmission. Under normal circumstances, this should never happen. However, it could happen if the other peer doesn't respect the credit, or if the skb overhead, which we recently began to take into account with commit 059b7dbd20a6 ("vsock/virtio: fix potential unbounded skb queue"), is too high. Fix this by resetting the connection and setting the local socket error to ENOBUFS when virtio_transport_recv_enqueue() can no longer queue a packet, so both peers are explicitly notified of the failure rather than silently losing data. Fixes: ae6fcfbf5f03 ("vsock/virtio: discard packets if credit is not respected") Cc: stable@vger.kernel.org Signed-off-by: Stefano Garzarella Link: https://patch.msgid.link/20260518090656.134588-2-sgarzare@redhat.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/vmw_vsock/virtio_transport_common.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 495c93cddcdc..65e2b2489234 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -1335,7 +1335,7 @@ virtio_transport_recv_connecting(struct sock *sk, return err; } -static void +static bool virtio_transport_recv_enqueue(struct vsock_sock *vsk, struct sk_buff *skb) { @@ -1350,10 +1350,8 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk, spin_lock_bh(&vvs->rx_lock); can_enqueue = virtio_transport_inc_rx_pkt(vvs, len); - if (!can_enqueue) { - free_pkt = true; + if (!can_enqueue) goto out; - } if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SEQ_EOM) vvs->msg_count++; @@ -1393,6 +1391,8 @@ virtio_transport_recv_enqueue(struct vsock_sock *vsk, spin_unlock_bh(&vvs->rx_lock); if (free_pkt) kfree_skb(skb); + + return can_enqueue; } static int @@ -1405,7 +1405,17 @@ virtio_transport_recv_connected(struct sock *sk, switch (le16_to_cpu(hdr->op)) { case VIRTIO_VSOCK_OP_RW: - virtio_transport_recv_enqueue(vsk, skb); + if (!virtio_transport_recv_enqueue(vsk, skb)) { + /* There is no more space to queue the packet, so let's + * close the connection; otherwise, we'll lose data. + */ + (void)virtio_transport_reset(vsk, skb); + virtio_transport_do_close(vsk, true); + sk->sk_err = ENOBUFS; + sk_error_report(sk); + vsock_remove_sock(vsk); + break; + } vsock_data_ready(sk); return err; case VIRTIO_VSOCK_OP_CREDIT_REQUEST: From a3529032afe2253117dcda3920af7cb2b40ed272 Mon Sep 17 00:00:00 2001 From: Jose Ignacio Tornos Martinez Date: Fri, 15 May 2026 11:24:09 -0700 Subject: [PATCH 087/697] ice: fix VF queue configuration with low MTU values commit 3ba4dd024d26372733d1c02e13e076c6016e3320 upstream. The ice driver's VF queue configuration validation rejects databuffer_size values below 1024 bytes, which prevents VFs from using MTU values below 871 bytes. The iavf driver calculates databuffer_size based on the MTU using: databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128) where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN). For MTU values below 871: MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected) MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted) The 1024-byte minimum seems unnecessarily restrictive, because the hardware supports databuffer_size as low as 128 bytes (the alignment boundary), which should allow MTU values down to the standard minimum of 68 bytes. I haven't found the reason why the limit was configured in the commit 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so with no more information and since it is working, change the minimum databuffer_size validation from 1024 to 128 bytes to allow standard low MTU values while still preventing invalid configurations. Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message") cc: stable@vger.kernel.org Signed-off-by: Jose Ignacio Tornos Martinez Reviewed-by: Jacob Keller Reviewed-by: Michal Swiatkowski Reviewed-by: Paul Menzel Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-3-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/virt/queues.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c index 370f6ec2a374..a82024bf388b 100644 --- a/drivers/net/ethernet/intel/ice/virt/queues.c +++ b/drivers/net/ethernet/intel/ice/virt/queues.c @@ -840,7 +840,7 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg) if (qpi->rxq.databuffer_size != 0 && (qpi->rxq.databuffer_size > ((16 * 1024) - 128) || - qpi->rxq.databuffer_size < 1024)) + qpi->rxq.databuffer_size < 128)) goto error_param; ring->rx_buf_len = qpi->rxq.databuffer_size; if (qpi->rxq.max_pkt_size > max_frame_size || From 614cacec60fe92d50abe532f011a7549a1c94169 Mon Sep 17 00:00:00 2001 From: Kyle Farnung Date: Wed, 13 May 2026 21:52:12 -0700 Subject: [PATCH 088/697] wifi: ath11k: clear shared SRNG pointer state on restart commit f51e4b3b5574ad8cb5b16b11f8a1452147ece87a upstream. LMAC rings reuse the shared rdp/wrp pointer buffers without going through the normal SRNG hw-init path that zeros non-LMAC ring pointers. After restart, ath11k_hal_srng_clear() can therefore hand stale hp/tp state from the previous firmware instance back to the new one. Clear the shared pointer buffers while keeping the allocations in place so restart still avoids reallocating SRNG DMA memory, but starts with fresh ring-pointer state. Fixes: 32be3ca4cf78b ("wifi: ath11k: HAL SRNG: don't deinitialize and re-initialize again") Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/CAOPSVF04q6uvVdq8GTRLHBrVMdpt9=o9wVcFMc6f-yhmSBcZqQ@mail.gmail.com/ Signed-off-by: Kyle Farnung Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260513-kfarnung-ath11k-srng-clear-pointer-state-v1-1-bc700dd8b333@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath11k/hal.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index 0c797b8d0a27..8330c9e7ac7d 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -1388,14 +1388,22 @@ EXPORT_SYMBOL(ath11k_hal_srng_deinit); void ath11k_hal_srng_clear(struct ath11k_base *ab) { - /* No need to memset rdp and wrp memory since each individual - * segment would get cleared in ath11k_hal_srng_src_hw_init() - * and ath11k_hal_srng_dst_hw_init(). + /* + * Preserve the shared pointer buffers, but clear the previous + * firmware instance's hp/tp state before handing them back to FW. + * LMAC rings reuse this shared memory without going through the + * normal SRNG hw-init path that zeros non-LMAC ring pointers. */ memset(ab->hal.srng_list, 0, sizeof(ab->hal.srng_list)); memset(ab->hal.shadow_reg_addr, 0, sizeof(ab->hal.shadow_reg_addr)); + if (ab->hal.rdp.vaddr) + memset(ab->hal.rdp.vaddr, 0, + sizeof(*ab->hal.rdp.vaddr) * HAL_SRNG_RING_ID_MAX); + if (ab->hal.wrp.vaddr) + memset(ab->hal.wrp.vaddr, 0, + sizeof(*ab->hal.wrp.vaddr) * HAL_SRNG_NUM_LMAC_RINGS); ab->hal.avail_blk_resource = 0; ab->hal.current_blk_index = 0; ab->hal.num_shadow_reg_configured = 0; From 6fe92651b44fd3cfc8dcfdaad0e82885c384dada Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 15 May 2026 15:14:57 +0300 Subject: [PATCH 089/697] wifi: iwlwifi: mvm: fix driver-set TX rates on old devices commit fb84b5cbcaab3ca0f4e961d92a40ed7f3aac483b upstream. On old devices such as 7265D, rates are still encoded in version 1 format, which doesn't use the CCK/OFDM rate index (0-3/0-7) but rather their PLCP value (e.g. 10 for 1 Mbps CCK rate.) While introducing v3 rates, I changed the driver from internally handling v1 rates and converting to v2, to internally handling v3 and converting to v1 or v2 according to the firmware. I accordingly changed the code in iwl_mvm_mac80211_idx_to_hwrate() to no longer have different values for different APIs. This was correct. However, I later reverted this part of the change, because it was reported that I had broken beacon rates, causing a FW assert/crash. This caused TX_CMD rates to be set incorrectly, potentially causing a warning when reported back from the device as having been used. Fix this (hopefully correctly now) by handling beacon rates in the TX_CMD that's embedded in the beacon template command separately. Restore iwl_mvm_mac80211_idx_to_hwrate() to return only the rate index, not PLCP value, fixing the real TX_CMD. Cc: stable@vger.kernel.org Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260515151351.7407e293dff7.I4ea1a17f8fe99c933d3f3e30d077cf4246125c3e@changeid Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 27 ++++++++++++------- .../net/wireless/intel/iwlwifi/mvm/utils.c | 14 +++------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 49ffc4ecee85..44380ebfe09d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -938,13 +938,18 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) { - u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10; + u16 flags = 0; if (rate_idx <= IWL_LAST_CCK_RATE) flags |= is_new_rate ? IWL_MAC_BEACON_CCK : IWL_MAC_BEACON_CCK_V1; + if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) + flags |= iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); + else + flags |= iwl_fw_rate_idx_to_plcp(rate_idx); + return flags; } @@ -973,6 +978,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_tx_info *info; + u32 rate_n_flags = 0; u8 rate; u32 tx_flags; @@ -992,18 +998,21 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx); - tx_params->rate_n_flags = - cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << - RATE_MCS_ANT_POS); + rate_n_flags |= BIT(mvm->mgmt_last_antenna_idx) << + RATE_MCS_ANT_POS; } rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif); - tx_params->rate_n_flags |= - cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate)); - if (rate == IWL_FIRST_CCK_RATE) - tx_params->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK_V1); + if (rate < IWL_FIRST_OFDM_RATE) + rate_n_flags |= RATE_MCS_MOD_TYPE_CCK; + else + rate_n_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; + + rate_n_flags |= iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate); + tx_params->rate_n_flags = iwl_mvm_v3_rate_to_fw(rate_n_flags, + mvm->fw_rates_ver); } int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index fa995e235d9b..63e84efdaa46 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2025 Intel Corporation + * Copyright (C) 2012-2014, 2018-2026 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -159,15 +159,9 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx) { - if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) - /* In the new rate legacy rates are indexed: - * 0 - 3 for CCK and 0 - 7 for OFDM. - */ - return (rate_idx >= IWL_FIRST_OFDM_RATE ? - rate_idx - IWL_FIRST_OFDM_RATE : - rate_idx); - - return iwl_fw_rate_idx_to_plcp(rate_idx); + return rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : + rate_idx; } u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac) From dc31c69476520bb4c2a208211a8d3c310a62c4d0 Mon Sep 17 00:00:00 2001 From: Sheroz Juraev Date: Sun, 15 Mar 2026 13:12:21 +0500 Subject: [PATCH 090/697] wifi: iwlwifi: mld: stop TX during firmware restart commit 2becb38a3e217ef2b2f42fddd7db7a25905ec291 upstream. When iwlwifi firmware crashes (e.g., NMI_INTERRUPT_UNKNOWN on Intel BE201/Wi-Fi 7), iwl_mld_nic_error() sets mld->fw_status.in_hw_restart to true. However, iwl_mld_tx_from_txq() does not check this flag before dequeuing frames from mac80211 and pushing them to the transport layer. Since the firmware is dead, iwl_trans_tx() returns -EIO for each frame, which then gets freed immediately. Under high-throughput conditions (e.g., Tailscale UDP traffic or active SSH sessions), this creates a tight dequeue-send-fail-free loop that wastes CPU cycles and generates rapid skb allocation churn, leading to memory pressure from slab fragmentation. The RX path already has this guard (iwl_mld_rx_mpdu checks in_hw_restart at rx.c:1906), and so does the TXQ allocation worker (iwl_mld_add_txqs_wk at tx.c:156). Add the same guard to iwl_mld_tx_from_txq() to stop all TX during firmware restart. Frames left in mac80211's TXQs are naturally drained after restart completes, when queue reallocation triggers iwl_mld_tx_from_txq() via iwl_mld_add_txq_list(), or when new upper-layer traffic invokes wake_tx_queue. Tested on ASUS Zenbook 14 UX3405CA with Intel BE201 (Wi-Fi 7) on kernel 6.19.5 where the firmware crashes approximately every 10-15 minutes under Tailscale traffic. Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Cc: stable@vger.kernel.org Signed-off-by: Sheroz Juraev Link: https://patch.msgid.link/20260315081221.2678478-1-goodmartiandev@gmail.com Signed-off-by: Miri Korenblit Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/intel/iwlwifi/mld/tx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index e3fb4fc4f452..c54ea3a91b66 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -964,6 +964,16 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) struct sk_buff *skb = NULL; u8 zero_addr[ETH_ALEN] = {}; + /* + * Don't transmit during firmware restart. The firmware is dead, + * so iwl_trans_tx() would return -EIO for each frame. Avoid the + * overhead of dequeuing from mac80211 only to immediately free + * the skbs, and the potential memory pressure from rapid skb + * allocation churn during high-throughput restart scenarios. + */ + if (unlikely(mld->fw_status.in_hw_restart)) + return; + /* * No need for threads to be pending here, they can leave the first * taker all the work. From 7725cd3b471740fd23d25ed1da722c671fb2a5d3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 12 May 2026 16:51:14 -0400 Subject: [PATCH 091/697] ipv4: raw: reject IP_HDRINCL packets with ihl < 5 commit 915fab69823a14c170dbaa3b41978768e0fe62fc upstream. raw_send_hdrinc() validates that the caller-supplied IPv4 header fits within the message length: iphlen = iph->ihl * 4; err = -EINVAL; if (iphlen > length) goto error_free; if (iphlen >= sizeof(*iph)) { /* fix up saddr, tot_len, id, csum, transport_header */ } It does not, however, reject ihl < 5. For such a packet the "if (iphlen >= sizeof(*iph))" branch is skipped, leaving the crafted iphdr untouched, but the packet is still handed to __ip_local_out() and onward. Downstream consumers that read iph->ihl assume a sane value: net/ipv4/ah4.c:ah_output() in particular subtracts sizeof(struct iphdr) from top_iph->ihl * 4 and passes the (signed-int-negative, then cast to size_t) result to memcpy(), producing an OOB access of length close to SIZE_MAX and a host kernel panic. An IPv4 header with ihl < 5 is malformed by definition (RFC 791: "Internet Header Length is the length of the internet header in 32 bit words ... Note that the minimum value for a correct header is 5."). The kernel should not be willing to inject such a packet into its own output path. Reject "iphlen < sizeof(*iph)" alongside the existing "iphlen > length" check. This matches the principle that locally constructed packets that re-enter the IP stack must pass the same basic sanity tests that a foreign packet would be subjected to. Once this lands, the "if (iphlen >= sizeof(*iph))" wrapper around the fixup branch becomes redundant; left in place to keep the patch minimal and backport-friendly. A follow-up can unwrap it. Note that commit 86f4c90a1c5c ("ipv4, ipv6: ensure raw socket message is big enough to hold an IP header") ensures the message buffer is large enough to hold an iphdr, but does not constrain the self-reported iph->ihl. Reachability: the malformed packet source is any caller with CAP_NET_RAW, including an unprivileged process in a user+net namespace on a kernel with CONFIG_USER_NS=y. The reproduced AH crash also requires a matching xfrm AH policy on the outgoing route; a container granted CAP_NET_ADMIN can install that state and policy in its netns. Loopback bypasses xfrm_output, so the trigger uses a real netdev. Reproduced on UML + KASAN: kernel-mode fault at addr 0x0 with memcpy_orig at the crash site. Same shape reproduces inside a rootless Docker container with --cap-add NET_ADMIN on a stock distro kernel. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Suggested-by: Herbert Xu Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/77ec2b5e8111961c2c39883c92e8aa2709039c17.1778614451.git.michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv4/raw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index d54ebb7df966..f79f4c29a043 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -390,7 +390,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, * in, reject the frame as invalid */ err = -EINVAL; - if (iphlen > length) + if (iphlen > length || iphlen < sizeof(*iph)) goto error_free; if (iphlen >= sizeof(*iph)) { From dfef79e09ed2f5df975c98547f97f5d7f8982a24 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 11:24:14 -0700 Subject: [PATCH 092/697] ixgbevf: fix use-after-free in VEPA multicast source pruning commit 5d49b568c188dc77199d8d2b959c91da8cc27cf1 upstream. ixgbevf_clean_rx_irq() prunes frames whose source MAC matches the VF's own address (VEPA multicast workaround) by freeing the skb and continuing to the next descriptor: dev_kfree_skb_irq(skb); continue; The skb pointer is declared outside the while loop and persists across iterations. Because the continue skips the "skb = NULL" reset at the bottom of the loop, the next iteration enters the "else if (skb)" path and calls ixgbevf_add_rx_frag() on the freed skb, dereferencing skb_shinfo(skb)->nr_frags - a use-after-free in NAPI softirq context. The sibling driver iavf already handles this correctly by nulling the pointer before continuing. Apply the same pattern here. I do not have ixgbevf hardware; the bug was found by static analysis (scan_drop_continue_loops.py + semgrep drop_continue_in_loop, multi-tool corroboration with the highest score in the scan). The UAF was confirmed under KASAN by loading a test module that reproduces the exact code pattern (alloc skb, kfree_skb, then read skb_shinfo(skb)->nr_frags): BUG: KASAN: slab-use-after-free in ixgbevf_uaf_test_init+0x100/0x1000 Read of size 8 at addr 000000006163ae78 by task insmod/30 freed 208-byte region [000000006163adc0, 000000006163ae90) QEMU emulates igb (82576) but not ixgbe (82599), and the igbvf VF driver does not include the VEPA source pruning path, so a full end-to-end reproduction with emulated hardware was not possible. Fixes: bad17234ba70 ("ixgbevf: Change receive model to use double buffered page based receives") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: Simon Horman Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-8-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index d5ce20f47def..c999abd78481 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -1221,6 +1221,7 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, ether_addr_equal(rx_ring->netdev->dev_addr, eth_hdr(skb)->h_source)) { dev_kfree_skb_irq(skb); + skb = NULL; continue; } From 9dcd4f5c99b491c37be90b0bd9988db48225fb75 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Tue, 19 May 2026 23:07:26 +0200 Subject: [PATCH 093/697] rbd: eliminate a race in lock_dwork draining on unmap commit 9fc75b71fdd38465c76c6f6a884cdd4ae3c72d90 upstream. Given how rbd_lock_add_request() and rbd_img_exclusive_lock() are written, lock_dwork may be (re)queued more than it's actually needed: for example in case a new I/O request comes in while we are in the middle of rbd_acquire_lock() on behalf of another I/O request. This is expected and with rbd_release_lock() preemptively canceling lock_dwork is benign under normal operation. A more problematic example is maybe_kick_acquire(): if (have_requests || delayed_work_pending(&rbd_dev->lock_dwork)) { dout("%s rbd_dev %p kicking lock_dwork\n", __func__, rbd_dev); mod_delayed_work(rbd_dev->task_wq, &rbd_dev->lock_dwork, 0); } It's not unrealistic for lock_dwork to get canceled right after delayed_work_pending() returns true and for mod_delayed_work() to requeue it right there anyway. This is a classic TOCTOU race. When it comes to unmapping the image, there is an implicit assumption of no self-initiated exclusive lock activity past the point of return from rbd_dev_image_unlock() which unlocks the lock if it happens to be held. This unlock is assumed to be final and lock_dwork (as well as all other exclusive lock tasks, really) isn't expected to get queued again. However, lock_dwork is canceled only in cancel_tasks_sync() (i.e. later in the unmap sequence) and on top of that the cancellation can get in effect nullified by maybe_kick_acquire(). This may result in rbd_acquire_lock() executing after rbd_dev_device_release() and rbd_dev_image_release() run and free and/or reset a bunch of things. One of the possible failure modes then is a violated rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); in rbd_dev_header_info() which is called via rbd_dev_refresh() from rbd_post_acquire_action(). Redo exclusive lock task draining to provide saner semantics and try to meet the assumptions around rbd_dev_image_unlock(). Cc: stable@vger.kernel.org Signed-off-by: Ilya Dryomov Reviewed-by: Viacheslav Dubeyko Signed-off-by: Greg Kroah-Hartman --- drivers/block/rbd.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 9236b5184bce..97cd8cf7bad3 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -4565,24 +4565,12 @@ static int rbd_register_watch(struct rbd_device *rbd_dev) return ret; } -static void cancel_tasks_sync(struct rbd_device *rbd_dev) -{ - dout("%s rbd_dev %p\n", __func__, rbd_dev); - - cancel_work_sync(&rbd_dev->acquired_lock_work); - cancel_work_sync(&rbd_dev->released_lock_work); - cancel_delayed_work_sync(&rbd_dev->lock_dwork); - cancel_work_sync(&rbd_dev->unlock_work); -} - /* * header_rwsem must not be held to avoid a deadlock with * rbd_dev_refresh() when flushing notifies. */ static void rbd_unregister_watch(struct rbd_device *rbd_dev) { - cancel_tasks_sync(rbd_dev); - mutex_lock(&rbd_dev->watch_mutex); if (rbd_dev->watch_state == RBD_WATCH_STATE_REGISTERED) __rbd_unregister_watch(rbd_dev); @@ -6548,10 +6536,18 @@ static int rbd_add_parse_args(const char *buf, static void rbd_dev_image_unlock(struct rbd_device *rbd_dev) { + dout("%s rbd_dev %p\n", __func__, rbd_dev); + + disable_delayed_work_sync(&rbd_dev->lock_dwork); + disable_work_sync(&rbd_dev->unlock_work); + down_write(&rbd_dev->lock_rwsem); if (__rbd_is_lock_owner(rbd_dev)) __rbd_release_lock(rbd_dev); up_write(&rbd_dev->lock_rwsem); + + flush_work(&rbd_dev->acquired_lock_work); + flush_work(&rbd_dev->released_lock_work); } /* From 5b906f31e977286888a9e31282589b545b249139 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Wed, 13 May 2026 14:05:06 -0400 Subject: [PATCH 094/697] lsm: hold cred_guard_mutex for lsm_set_self_attr() commit 4a9b16541ad3faf8bccb398532bf3f8b6bbf1188 upstream. Just as proc_pid_attr_write() already does before calling the LSM hook. This only matters for SELinux and AppArmor which check whether the process is being ptraced and if so, whether to allow the transition. Cc: stable@vger.kernel.org Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Signed-off-by: Paul Moore Signed-off-by: Greg Kroah-Hartman --- security/lsm_syscalls.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/security/lsm_syscalls.c b/security/lsm_syscalls.c index 8440948a690c..b3887c85a2ba 100644 --- a/security/lsm_syscalls.c +++ b/security/lsm_syscalls.c @@ -55,7 +55,14 @@ u64 lsm_name_to_attr(const char *name) SYSCALL_DEFINE4(lsm_set_self_attr, unsigned int, attr, struct lsm_ctx __user *, ctx, u32, size, u32, flags) { - return security_setselfattr(attr, ctx, size, flags); + int rc; + + rc = mutex_lock_interruptible(¤t->signal->cred_guard_mutex); + if (rc < 0) + return rc; + rc = security_setselfattr(attr, ctx, size, flags); + mutex_unlock(¤t->signal->cred_guard_mutex); + return rc; } /** From 47a4cf2229be379cf88f92e32e1240337cd6273f Mon Sep 17 00:00:00 2001 From: Sam Daly Date: Wed, 13 May 2026 18:42:53 +0200 Subject: [PATCH 095/697] octeontx2-af: CGX: add bounds check to cgx_speed_mbps index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c0bf0a4f3f1f5f57aa83e1400ba4f56f0abfd542 upstream. cgx_speed_mbps has 13 elements but RESP_LINKSTAT_SPEED can yield values 0-15. If it returns a value >= 13, this causes an out-of-bounds array access. Add a bounds check and default to speed 0 if the index is out of range. Fixes: 61071a871ea6 ("octeontx2-af: Forward CGX link notifications to PFs") Cc: Sunil Goutham Cc: Linu Cherian Cc: Geetha sowjanya Cc: hariprasad Cc: Subbaraya Sundeep Cc: Andrew Lunn Cc: stable Signed-off-by: Sam Daly Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026051352-refined-demise-e88d@gregkh Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 81b55f1416e0..c6b5f6564ed0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -1294,13 +1294,18 @@ static inline void link_status_user_format(u64 lstat, struct cgx_link_user_info *linfo, struct cgx *cgx, u8 lmac_id) { + unsigned int speed; + linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); - linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); linfo->lmac_type_id = FIELD_GET(RESP_LINKSTAT_LMAC_TYPE, lstat); + speed = FIELD_GET(RESP_LINKSTAT_SPEED, lstat); + linfo->speed = speed < ARRAY_SIZE(cgx_speed_mbps) ? + cgx_speed_mbps[speed] : 0; + if (linfo->lmac_type_id >= LMAC_MODE_MAX) { dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", linfo->lmac_type_id, cgx->cgx_id, lmac_id); From 8864b664d0443ecb8e56690e5546fcda5fe5e81b Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 13 May 2026 23:13:20 +0800 Subject: [PATCH 096/697] octeontx2-pf: fix double free in rvu_rep_rsrc_init() commit e8fb3de2a8effcaf62bec2c56b93d8bb480371d1 upstream. rvu_rep_rsrc_init() allocates queue memory before calling otx2_init_hw_resources(). When hardware resource setup fails, otx2_init_hw_resources() already unwinds the partially initialized SQ, CQ, and aura state before returning an error. The representor error path then calls otx2_free_hw_resources() again and can free the same resources a second time. Fix this by splitting the cleanup labels so that a failure from otx2_init_hw_resources() only releases queue memory. Keep the otx2_free_hw_resources() call for failures that happen after hardware resource initialization completed successfully. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc3. Runtime validation was not performed because reproducing this path requires OcteonTX2 representor hardware. Fixes: 3937b7308d4f ("octeontx2-pf: Create representor netdev") Cc: stable@vger.kernel.org # v6.13+ Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Geetha sowjanya Link: https://patch.msgid.link/20260513151320.213260-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/nic/rep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c index b476733a0234..3271ab5539a5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/rep.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/rep.c @@ -609,7 +609,7 @@ static int rvu_rep_rsrc_init(struct otx2_nic *priv) err = otx2_init_hw_resources(priv); if (err) - goto err_free_rsrc; + goto err_free_mem; /* Set maximum frame size allowed in HW */ err = otx2_hw_set_mtu(priv, priv->hw.max_mtu); @@ -621,6 +621,7 @@ static int rvu_rep_rsrc_init(struct otx2_nic *priv) err_free_rsrc: otx2_free_hw_resources(priv); +err_free_mem: otx2_free_queue_mem(qset); return err; } From f1bafd35f11b3aca1c7bb38da173b8787364a04c Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Fri, 15 May 2026 11:24:16 -0700 Subject: [PATCH 097/697] igc: fix potential skb leak in igc_fpe_xmit_smd_frame() commit e935c37b8a94bb256fada6395a5d05e1c0c6bdaf upstream. When igc_fpe_init_tx_descriptor() fails, no one takes care of an allocated skb, leaking it. [1] Use dev_kfree_skb_any() on failure. Tested on an I226 adapter with the following command, while injecting faults in igc_fpe_init_tx_descriptor() to trigger the error path. # ethtool --set-mm $DEV verify-enabled on tx-enabled on pmac-enabled on [1] unreferenced object 0xffff888113c6cdc0 (size 224): ... backtrace (crc be3d3fda): kmem_cache_alloc_node_noprof+0x3b1/0x410 __alloc_skb+0xde/0x830 igc_fpe_xmit_smd_frame.isra.0+0xad/0x1b0 igc_fpe_send_mpacket+0x37/0x90 ethtool_mmsv_verify_timer+0x15e/0x300 Cc: stable@vger.kernel.org Fixes: 5422570c0010 ("igc: add support for frame preemption verification") Signed-off-by: Kohei Enju Reviewed-by: Simon Horman Reviewed-by: Faizal Rahim Tested-by: Avigail Dahan Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-10-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/igc/igc_tsn.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 8a110145bfee..02dd9f0290a3 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -109,10 +109,16 @@ static int igc_fpe_xmit_smd_frame(struct igc_adapter *adapter, __netif_tx_lock(nq, cpu); err = igc_fpe_init_tx_descriptor(ring, skb, type); - igc_flush_tx_descriptors(ring); + if (err) + goto err_free_skb_any; + igc_flush_tx_descriptors(ring); __netif_tx_unlock(nq); + return 0; +err_free_skb_any: + __netif_tx_unlock(nq); + dev_kfree_skb_any(skb); return err; } From 9c9d00d81b41cb637dd0440e4d995861d052e5c8 Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Fri, 15 May 2026 11:24:08 -0700 Subject: [PATCH 098/697] ice: fix locking around wait_event_interruptible_locked_irq commit 89bbff099bfc94888eb942d5b981592bbbe0c856 upstream. Commit 50327223a8bb ("ice: add lock to protect low latency interface") introduced a wait queue used to protect the low latency timer interface. The queue is used with the wait_event_interruptible_locked_irq macro, which unlocks the wait queue lock while sleeping. The irq variant uses spin_lock_irq and spin_unlock_irq to manage this. The wait queue lock was previously locked using spin_lock_irqsave. This difference in lock variants could lead to issues, since wait_event would unlock the wait queue and restore interrupts while sleeping. The ice_read_phy_tstamp_ll_e810() function is ultimately called through ice_read_phy_tstamp, which is called from ice_ptp_process_tx_tstamp or ice_ptp_clear_unexpected_tx_ready. The former is called through the miscellaneous IRQ thread function, while the latter is called from the service task work queue thread. Neither of these functions has interrupts disabled, so use spin_lock_irq instead of spin_lock_irqsave. Fixes: 50327223a8bb ("ice: add lock to protect low latency interface") Cc: stable@vger.kernel.org Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/netdev/20250109181823.77f44c69@kernel.org/ Signed-off-by: Jacob Keller Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-2-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index d461e00d15e9..30b783939816 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -4503,18 +4503,17 @@ static int ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo) { struct ice_e810_params *params = &hw->ptp.phy.e810; - unsigned long flags; u32 val; int err; - spin_lock_irqsave(¶ms->atqbal_wq.lock, flags); + spin_lock_irq(¶ms->atqbal_wq.lock); /* Wait for any pending in-progress low latency interrupt */ err = wait_event_interruptible_locked_irq(params->atqbal_wq, !(params->atqbal_flags & ATQBAL_FLAGS_INTR_IN_PROGRESS)); if (err) { - spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->atqbal_wq.lock); return err; } @@ -4529,7 +4528,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo) REG_LL_PROXY_H); if (err) { ice_debug(hw, ICE_DBG_PTP, "Failed to read PTP timestamp using low latency read\n"); - spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->atqbal_wq.lock); return err; } @@ -4539,7 +4538,7 @@ ice_read_phy_tstamp_ll_e810(struct ice_hw *hw, u8 idx, u8 *hi, u32 *lo) /* Read the low 32 bit value and set the TS valid bit */ *lo = rd32(hw, REG_LL_PROXY_L) | TS_VALID; - spin_unlock_irqrestore(¶ms->atqbal_wq.lock, flags); + spin_unlock_irq(¶ms->atqbal_wq.lock); return 0; } From 0b9431b972a0d0933b5b2f31079a037fe17d01dc Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Fri, 15 May 2026 11:24:10 -0700 Subject: [PATCH 099/697] ice: fix setting promisc mode while adding VID filter commit ebc8de716c9ec2be384abdc2dd866da26c6580d1 upstream. There are at least two paths through which VSI promiscuous mode can be independently configured via ice_fltr_set_vsi_promisc(): - ice_vlan_rx_add_vid() (netdev op) - ice_service_task() -> ... -> ice_set_promisc() Both paths may try to program promiscuous mode concurrently. One such scenario is: 1. Add ice netdev to bond 2. Add the bond netdev to bridge 3. ice netdev enters allmulticast mode (IFF_ALLMULTI) 4. Service task programs promisc mode filter 5. Bridge -> bond calls ice_vlan_rx_add_vid() Crucially, ice_vlan_rx_add_vid() fails if ice_fltr_set_vsi_promisc() returns any error, including -EEXIST. This causes VLAN filtering setup to fail on the bond interface. ice_set_promisc() already handles -EEXIST correctly. Fix by adding the same -EEXIST check to ice_vlan_rx_add_vid(): if the promisc filter is already programmed, continue without returning error. Fixes: 1273f89578f2 ("ice: Fix broken IFF_ALLMULTI handling") Cc: stable@vger.kernel.org Signed-off-by: Marcin Szycik Signed-off-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Rinitha S (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-4-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index c064c3653c54..2d78d9838518 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -3764,7 +3764,7 @@ int ice_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) ret = ice_fltr_set_vsi_promisc(&vsi->back->hw, vsi->idx, ICE_MCAST_VLAN_PROMISC_BITS, vid); - if (ret) + if (ret && ret != -EEXIST) goto finish; } From 50884c2afd7a016ef56667c4079c2d27a261bbb3 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Fri, 15 May 2026 11:24:13 -0700 Subject: [PATCH 100/697] ice: restore PTP Rx timestamp config after ethtool set-channels commit 975b564d195b13ca6ee1ef5e6a9561734898eb17 upstream. When ethtool -L changes queue counts, ice_vsi_recfg_qs() closes and rebuilds the VSI, reallocating Rx rings. The newly allocated rings have ptp_rx cleared, so RX hardware timestamps are no longer attached to skb until hwtstamp configuration is applied again. Restore timestamp mode after ice_vsi_open() in the queue reconfiguration path, matching reset/rebuild behavior and ensuring newly rebuilt Rx rings have PTP RX timestamping re-enabled. Testing hints: - run ptp4l application in client synchronization mode: ptp4l -i ethX -m -s - run PTP traffic - change queue number on ethX netdev interface: ethtool -L ethX combined new_queue_size - observe ptp4l output - expected result: no "received DELAY_REQ without timestamp" messages Fixes: 77a781155a65 ("ice: enable receive hardware timestamping") Cc: stable@vger.kernel.org Reviewed-by: Aleksandr Loktionov Signed-off-by: Grzegorz Nitka Reviewed-by: Simon Horman Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-7-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/intel/ice/ice_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 2d78d9838518..d42b65e29e48 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -4186,6 +4186,12 @@ int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked) } ice_pf_dcb_recfg(pf, locked); ice_vsi_open(vsi); + /* Rx rings are reallocated during VSI rebuild and lose their ptp_rx + * flag. Restore timestamp mode so newly allocated rings are set up + * for hardware Rx timestamping. + */ + if (test_bit(ICE_FLAG_PTP_SUPPORTED, pf->flags)) + ice_ptp_restore_timestamp_mode(pf); goto done; rebuild_err: From 6cfae4914439878b8acb35c7e3b40096eeb2ad9c Mon Sep 17 00:00:00 2001 From: John Walker Date: Thu, 7 May 2026 17:07:20 -0600 Subject: [PATCH 101/697] wifi: cfg80211: advance loop vars in cfg80211_merge_profile() commit 7666dbb1bacc4ba522b96740cba7283d243d16e1 upstream. cfg80211_merge_profile() reassembles a Multi-BSSID non-transmitted BSS profile that has been split across multiple consecutive MBSSID elements. Its while-loop calls cfg80211_get_profile_continuation(ie, ielen, mbssid_elem, sub_elem) but never advances mbssid_elem or sub_elem inside the body. Each iteration therefore searches for a continuation that follows the same fixed pair; the helper returns the same next_mbssid; and the same next_sub bytes are memcpy()'d into merged_ie at a growing offset until the buffer fills. Advance both mbssid_elem and sub_elem to the just-consumed continuation so the next call to cfg80211_get_profile_continuation() searches for a further continuation beyond it (or returns NULL when none exists). A specially-crafted malicious beacon can take advantage of this bug to cause the kernel to spend an excessive amount of time in cfg80211_merge_profile (up to as much as 2ms per beacon received), which could theoretically be abused in some way. Cc: stable@vger.kernel.org Fixes: fe806e4992c9 ("cfg80211: support profile split between elements") Signed-off-by: John Walker Link: https://patch.msgid.link/20260507230720.64783-1-johnwalker0@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/wireless/scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9a0c02c23dc5..4a1cdfc3221c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2475,6 +2475,9 @@ size_t cfg80211_merge_profile(const u8 *ie, size_t ielen, memcpy(merged_ie + copied_len, next_sub->data, next_sub->datalen); copied_len += next_sub->datalen; + + mbssid_elem = next_mbssid; + sub_elem = next_sub; } return copied_len; From acdff9907478e82208475b1151700d0b71dcdc63 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 18 May 2026 18:51:30 +0200 Subject: [PATCH 102/697] af_unix: Fix UAF read of tail->len in unix_stream_data_wait() commit be309f8eae8b474a4a617eaae01324da996fc719 upstream. unix_stream_data_wait() does skb_peek_tail(&sk->sk_receive_queue) without holding any lock that prevents SKBs on that queue from being dequeued and freed. This has been the case since commit 79f632c71bea ("unix/stream: fix peeking with an offset larger than data in queue"). The first consequence of this is that the pointer comparison `tail != last` can be false even if `last` semantically refers to an already-freed SKB while `tail` is a new SKB allocated at the same address; which can cause unix_stream_data_wait() to wrongly keep blocking after new data has arrived, but only in a weird scenario where a peeking recv() and a normal recv() on the same socket are racing, which is probably not a real problem. But since commit 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix sockets"), `tail` is actually dereferenced, which can cause UAF in the following race scenario (where test_setup() runs single-threaded, and afterwards, test_thread1() and test_thread2() run concurrently in two threads: ``` static int socks[2]; void test_setup(void) { socketpair(AF_UNIX, SOCK_STREAM, 0, socks); send(socks[1], "A", 1, 0); int peekoff = 1; setsockopt(socks[0], SOL_SOCKET, SO_PEEK_OFF, &peekoff, sizeof(peekoff)); } void test_thread1(void) { char dummy; recv(socks[0], &dummy, 1, MSG_PEEK); } void test_thread2(void) { char dummy; recv(socks[0], &dummy, 1, 0); shutdown(socks[1], SHUT_WR); } ``` when racing like this: ``` thread1 thread2 unix_stream_read_generic mutex_lock(&u->iolock) skb_peek(&sk->sk_receive_queue) skb_peek_next(skb, &sk->sk_receive_queue) mutex_unlock(&u->iolock) unix_stream_read_generic unix_state_lock(sk) skb_peek(&sk->sk_receive_queue) unix_state_unlock(sk) unix_stream_data_wait unix_state_lock(sk) tail = skb_peek_tail(&sk->sk_receive_queue) spin_lock(&sk->sk_receive_queue.lock) __skb_unlink(skb, &sk->sk_receive_queue) spin_unlock(&sk->sk_receive_queue.lock) consume_skb(skb) [frees the SKB] `tail != last`: false `tail`: true `tail->len != last_len` ***UAF*** ``` Fix the UAF by removing the read of tail->len; checking tail->len would only make sense if SKBs in the receive queue of a UNIX socket could grow, which can no longer happen. Kuniyuki explained: > When commit 869e7c62486e ("net: af_unix: implement stream sendpage > support") added sendpage() support, data could be appended to the last > skb in the receiver's queue. > > That's why we needed to check if the length of the last skb was changed > while waiting for new data in unix_stream_data_wait(). > > However, commit a0dbf5f818f9 ("af_unix: Support MSG_SPLICE_PAGES") and > commit 57d44a354a43 ("unix: Convert unix_stream_sendpage() to use > MSG_SPLICE_PAGES") refactored sendmsg(), and now data is always added > to a new skb. That means this fix is not suitable for kernels before 6.5. Fixes: 2b514574f7e8 ("net: af_unix: implement splice for stream af_unix sockets") Cc: stable@vger.kernel.org # 6.5.x Signed-off-by: Jann Horn Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260518-b4-unix-recv-wait-hotfix-v2-1-83e29ce8ad31@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/unix/af_unix.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 33ed8ecb556d..faf04d1b6c01 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2716,8 +2716,7 @@ static int unix_read_skb(struct sock *sk, skb_read_actor_t recv_actor) * Sleep until more data has arrived. But check for races.. */ static long unix_stream_data_wait(struct sock *sk, long timeo, - struct sk_buff *last, unsigned int last_len, - bool freezable) + struct sk_buff *last, bool freezable) { unsigned int state = TASK_INTERRUPTIBLE | freezable * TASK_FREEZABLE; struct sk_buff *tail; @@ -2730,7 +2729,6 @@ static long unix_stream_data_wait(struct sock *sk, long timeo, tail = skb_peek_tail(&sk->sk_receive_queue); if (tail != last || - (tail && tail->len != last_len) || sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN) || signal_pending(current) || @@ -2923,7 +2921,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, int flags = state->flags; bool check_creds = false; struct scm_cookie scm; - unsigned int last_len; struct unix_sock *u; int copied = 0; int err = 0; @@ -2969,7 +2966,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, goto unlock; } last = skb = skb_peek(&sk->sk_receive_queue); - last_len = last ? last->len : 0; again: #if IS_ENABLED(CONFIG_AF_UNIX_OOB) @@ -3003,8 +2999,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, mutex_unlock(&u->iolock); - timeo = unix_stream_data_wait(sk, timeo, last, - last_len, freezable); + timeo = unix_stream_data_wait(sk, timeo, last, freezable); if (signal_pending(current)) { err = sock_intr_errno(timeo); @@ -3021,7 +3016,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, while (skip >= unix_skb_len(skb)) { skip -= unix_skb_len(skb); last = skb; - last_len = skb->len; skb = skb_peek_next(skb, &sk->sk_receive_queue); if (!skb) goto again; @@ -3096,7 +3090,6 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state, skip = 0; last = skb; - last_len = skb->len; unix_state_lock(sk); skb = skb_peek_next(skb, &sk->sk_receive_queue); if (skb) From 2dd9304727c7041df0a599595910bdbe02ad03c5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 11:17:18 -0400 Subject: [PATCH 103/697] wifi: mac80211: consume only present negotiated TTLM maps commit a6e6ccd5bd07155c2add6c74ce1a5e68ad3b95ea upstream. ieee80211_tid_to_link_map_size_ok() validates negotiated TTLM elements against the number of link-map entries indicated by link_map_presence. ieee80211_parse_neg_ttlm() must consume the same layout. The parser advanced its cursor for every TID, including TIDs whose presence bit is clear and therefore have no map bytes in the element. A sparse map can then make a later present TID read past the validated element. The bad bytes land in neg_ttlm->{up,down}link[tid] but are gated by valid_links before being applied to driver state, so a peer cannot turn the read into a policy change. Under KUnit + KASAN with an exact-sized element allocation the OOB read is reported as a slab-out-of-bounds; whether the same trigger fires under the production RX path depends on surrounding allocator state. Advance the cursor only when the current TID has a map present. Fixes: 8f500fbc6c65 ("wifi: mac80211: process and save negotiated TID to Link mapping request") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260515151719.1317659-2-michael.bommarito@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bcc4090ddc1a..ce247dde048c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -7959,6 +7959,7 @@ ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, "No active links for TID %d", tid); return -EINVAL; } + pos += map_size; } else { map = 0; } @@ -7977,7 +7978,6 @@ ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata, default: return -EINVAL; } - pos += map_size; } return 0; } From 5e7d9d0805e58fa3760894e73115b7a74024fd07 Mon Sep 17 00:00:00 2001 From: Zhihao Cheng Date: Tue, 19 May 2026 17:18:05 +0800 Subject: [PATCH 104/697] cifs: Fix busy dentry used after unmounting commit c68337442f03953237a94577beb468ab2662a851 upstream. Since commit 340cea84f691c ("cifs: open files should not hold ref on superblock"), cifs file only holds the dentry ref_cnt, the cifs file close work(cfile->deferred) could be executed after unmounting, which will trigger a warning in generic_shutdown_super: BUG: Dentry 00000000a14a6845{i=c,n=file} still in use (1) [unmount of cifs cifs] The detailed processs is: process A process B kworker fd = open(PATH) vfs_open file->__f_path = *path // dentry->d_lockref.count = 1 cifs_open cifs_new_fileinfo cfile->dentry = dget(dentry) // dentry->d_lockref.count = 2 close(fd) __fput cifs_close queue_delayed_work(deferredclose_wq, cfile->deferred) dput(dentry) // dentry->d_lockref.count = 1 smb2_deferred_work_close _cifsFileInfo_put list_del(&cifs_file->flist) umount cleanup_mnt deactivate_super cifs_kill_sb cifs_close_all_deferred_files_sb cifs_close_all_deferred_files // cannot find cfile, skip _cifsFileInfo_put kill_anon_super generic_shutdown_super shrink_dcache_for_umount umount_check WARN ! // dentry->d_lockref.count = 1 cifsFileInfo_put_final dput(cifs_file->dentry) // dentry->d_lockref.count = 0 Fix it by flushing 'deferredclose_wq' before calling kill_anon_super. Fetch a reproducer in https://bugzilla.kernel.org/show_bug.cgi?id=221548. Fixes: 340cea84f691c ("cifs: open files should not hold ref on superblock") Cc: stable@vger.kernel.org Reviewed-by: Shyam Prasad N Signed-off-by: Zhihao Cheng Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/cifsfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 4b34a4304edb..9059c2efbcc0 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -338,6 +338,8 @@ static void cifs_kill_sb(struct super_block *sb) /* Wait for all pending oplock breaks to complete */ flush_workqueue(cifsoplockd_wq); + /* Wait for all opened files to release */ + flush_workqueue(deferredclose_wq); /* finally release root dentry */ dput(cifs_sb->root); From 798183376d9d3e278a270ea0e75a5769c8f145d9 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 21 May 2026 13:49:14 +0900 Subject: [PATCH 105/697] tracing: Do not call map->ops->elt_free() if elt_alloc() fails commit 8f0f5c4fb9df0e19a341e0c6ed8dc4fda9124f03 upstream. In paths where tracing_map_elt_alloc() failed to allocate objects, the map->ops->elt_alloc() call was never successful. In this case, map->ops->elt_free() should not be called. Link: https://sashiko.dev/#/patchset/20260520223101.34710-1-rosenp%40gmail.com Cc: stable@vger.kernel.org Cc: Tom Zanussi Cc: Mathieu Desnoyers Cc: Rosen Penev Reported-by: Sashiko Fixes: 2734b629525a ("tracing: Add per-element variable support to tracing_map") Link: https://patch.msgid.link/177933895460.108746.5396070821443932634.stgit@devnote2 Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/tracing_map.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c index 7f8da4dab69d..ba52813ce51b 100644 --- a/kernel/trace/tracing_map.c +++ b/kernel/trace/tracing_map.c @@ -386,13 +386,11 @@ static void tracing_map_elt_init_fields(struct tracing_map_elt *elt) } } -static void tracing_map_elt_free(struct tracing_map_elt *elt) +static void __tracing_map_elt_free(struct tracing_map_elt *elt) { if (!elt) return; - if (elt->map->ops && elt->map->ops->elt_free) - elt->map->ops->elt_free(elt); kfree(elt->fields); kfree(elt->vars); kfree(elt->var_set); @@ -400,6 +398,17 @@ static void tracing_map_elt_free(struct tracing_map_elt *elt) kfree(elt); } +static void tracing_map_elt_free(struct tracing_map_elt *elt) +{ + if (!elt) + return; + + /* Only objects initialized with alloc_elt() should be passed to free_elt().*/ + if (elt->map->ops && elt->map->ops->elt_free) + elt->map->ops->elt_free(elt); + __tracing_map_elt_free(elt); +} + static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map) { struct tracing_map_elt *elt; @@ -444,7 +453,7 @@ static struct tracing_map_elt *tracing_map_elt_alloc(struct tracing_map *map) } return elt; free: - tracing_map_elt_free(elt); + __tracing_map_elt_free(elt); return ERR_PTR(err); } From 240373425e2d09928af534089f5c569ae2084a7b Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Fri, 15 May 2026 14:37:29 +0100 Subject: [PATCH 106/697] arm64: probes: Handle probes on hinted conditional branch instructions commit 2ccd8ff980b50e842481bae71102fa3883fc4377 upstream. BC.cond instructions introduced by FEAT_HBC cannot be executed out-of-line, like other branch instructions. However, they can be simulated in the same way as B.cond instructions. Extend the B.cond decoder mask to match BC.cond instructions as well, and handle them using the existing B.cond simulation path. Fixes: 7f86d128e437 ("arm64: add HWCAP for FEAT_HBC (hinted conditional branches)") Cc: Signed-off-by: Vladimir Murzin Signed-off-by: Catalin Marinas Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/insn.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index 18c7811774d3..032cd7b76b48 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -409,7 +409,7 @@ __AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x34000000) __AARCH64_INSN_FUNCS(cbnz, 0x7F000000, 0x35000000) __AARCH64_INSN_FUNCS(tbz, 0x7F000000, 0x36000000) __AARCH64_INSN_FUNCS(tbnz, 0x7F000000, 0x37000000) -__AARCH64_INSN_FUNCS(bcond, 0xFF000010, 0x54000000) +__AARCH64_INSN_FUNCS(bcond, 0xFF000000, 0x54000000) __AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001) __AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002) __AARCH64_INSN_FUNCS(smc, 0xFFE0001F, 0xD4000003) From 0680f511926589206f81f57f76ce131d7741a316 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 09:25:19 -0400 Subject: [PATCH 107/697] KVM: arm64: vgic-its: Reject restored DTE with out-of-range num_eventid_bits commit 9ce754ed8e7ab4e3999767ce1505f85c449ccb07 upstream. Userspace can restore an ITS Device Table Entry whose Size field encodes more EventID bits than the virtual ITS supports. The live MAPD path rejects that state, but vgic_its_restore_dte() accepts it and stores the out-of-range value in dev->num_eventid_bits. Reject restored DTEs with num_eventid_bits > VITS_TYPER_IDBITS before allocating the device. This mirrors the MAPD check and prevents the restored state from reaching vgic_its_restore_itt(), where the unchecked value can be converted into an oversized scan_its_table() range. Fixes: 57a9a117154c ("KVM: arm64: vgic-its: Device table save/restore") Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/r/20260519132519.2142458-1-michael.bommarito@gmail.com Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic-its.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index 3f1c4b10fed9..a4c0fb7f02ea 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -2307,6 +2307,10 @@ static int vgic_its_restore_dte(struct vgic_its *its, u32 id, /* dte entry is valid */ offset = (entry & KVM_ITS_DTE_NEXT_MASK) >> KVM_ITS_DTE_NEXT_SHIFT; + /* Mimic the MAPD behaviour and reject invalid EID bits. */ + if (num_eventid_bits > VITS_TYPER_IDBITS) + return -EINVAL; + if (!vgic_its_check_id(its, baser, id, NULL)) return -EINVAL; From 7023900b4988fb6f4a59d304d878003ff562e98d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Tue, 19 May 2026 09:50:42 -0400 Subject: [PATCH 108/697] KVM: arm64: vgic: Free private_irqs when init fails after allocation commit f19c354dbd457759dfcf1195ab4bdba2bb568323 upstream. Companion to commit 250f25367b58 ("KVM: arm64: Tear down vGIC on failed vCPU creation"), which added the missing kvm_vgic_vcpu_destroy() call to the kvm_share_hyp() failure path in kvm_arch_vcpu_create(). The kvm_vgic_vcpu_init() failure path immediately above it has the same shape and still needs the same cleanup. Call kvm_vgic_vcpu_destroy() when kvm_vgic_vcpu_init() fails so private IRQs allocated before a redistributor iodev registration failure are released before the failed vCPU is freed. Fixes: 03b3d00a70b5 ("KVM: arm64: vgic: Allocate private interrupts on demand") Cc: stable@vger.kernel.org Cc: Will Deacon Reviewed-by: Yuan Yao Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://lore.kernel.org/r/20260519135042.2219239-1-michael.bommarito@gmail.com Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index afc77977d4b9..e59cb36b5f36 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -490,8 +490,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) kvm_destroy_mpidr_data(vcpu->kvm); err = kvm_vgic_vcpu_init(vcpu); - if (err) + if (err) { + kvm_vgic_vcpu_destroy(vcpu); return err; + } err = kvm_share_hyp(vcpu, vcpu + 1); if (err) From 94ade38f317ea086a181db0e6b69c574b3b70a5e Mon Sep 17 00:00:00 2001 From: Tina Zhang Date: Fri, 22 May 2026 12:00:14 +0800 Subject: [PATCH 109/697] KVM: SVM: Disable AVIC IPI virtualization on Hygon Family 18h (erratum #1235) commit 9a12fa5213cfc391e0eed63902d3be98f0913765 upstream. Hygon Family 18h CPUs are derived from AMD Family 17h (Zen1) silicon and share the same erratum #1235: hardware may read a stale IsRunning=1 bit during ICR write emulation and silently fail to generate an AVIC_IPI_FAILURE_TARGET_NOT_RUNNING VM-Exit on the sending vCPU. The absence of the VM-Exit causes KVM to miss the required wakeup of blocking target vCPUs, leading to hung vCPUs and unbounded delays in guest execution. Extend the existing AMD Family 17h erratum #1235 workaround to also cover Hygon Family 18h. With IPI virtualization disabled, KVM never sets IsRunning=1 in the Physical ID table, so every non-self IPI generates a VM-Exit and is correctly emulated. Fixes: 8de4a1c8164e ("KVM: SVM: Disable (x2)AVIC IPI virtualization if CPU has erratum #1235") Cc: Signed-off-by: Tina Zhang Message-ID: <20260522040014.3380201-1-zhang_wei@open-hieco.net> Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index ba6e9485e824..ba844088581f 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -1252,12 +1252,14 @@ bool __init avic_hardware_setup(void) svm_x86_ops.allow_apicv_in_x2apic_without_x2apic_virtualization = true; /* - * Disable IPI virtualization for AMD Family 17h CPUs (Zen1 and Zen2) - * due to erratum 1235, which results in missed VM-Exits on the sender - * and thus missed wake events for blocking vCPUs due to the CPU - * failing to see a software update to clear IsRunning. + * Disable IPI virtualization for AMD Family 17h (Zen1 and Zen2) and + * Hygon Family 18h (derived from AMD Zen1) CPUs due to erratum 1235, + * which results in missed VM-Exits on the sender and thus missed wake + * events for blocking vCPUs due to the CPU failing to see a software + * update to clear IsRunning. */ - enable_ipiv = enable_ipiv && boot_cpu_data.x86 != 0x17; + if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) + enable_ipiv = false; amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier); From 77071943c752e2133dd6ea1f9933368246a49358 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 14 May 2026 19:36:40 +0200 Subject: [PATCH 110/697] riscv: kvm: return SBI_ERR_FAILURE for pmu_snapshot_set_shmem() when OOM commit 0835ee26938e15eccd70f7d33da386b6490f9449 upstream. kvm_riscv_vcpu_pmu_snapshot_set_shmem() returned -ENOMEM from the SBI extension handler, which caused kvm_riscv_vcpu_sbi_ecall() to abort KVM_RUN and surface the error to userspace instead of ompleting the ECALL with a negative SBI error in a0. Use SBI_ERR_FAILURE and the normal retdata path, matching other PMU handlers and kvm_sbi_ext_pmu_handler comment. Fixes: c2f41ddbcdd7 ("RISC-V: KVM: Implement SBI PMU Snapshot feature") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260514173642.41448-1-osama.abdelkader@gmail.com Signed-off-by: Anup Patel Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kvm/vcpu_pmu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index a2fae70ee174..aeb16dd78035 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -431,8 +431,10 @@ int kvm_riscv_vcpu_pmu_snapshot_set_shmem(struct kvm_vcpu *vcpu, unsigned long s } kvpmu->sdata = kzalloc(snapshot_area_size, GFP_ATOMIC); - if (!kvpmu->sdata) - return -ENOMEM; + if (!kvpmu->sdata) { + sbiret = SBI_ERR_FAILURE; + goto out; + } /* No need to check writable slot explicitly as kvm_vcpu_write_guest does it internally */ if (kvm_vcpu_write_guest(vcpu, saddr, kvpmu->sdata, snapshot_area_size)) { From 4f087193b5ff0211ce5fa567db3184fb31a53f65 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 14 May 2026 19:36:41 +0200 Subject: [PATCH 111/697] riscv: kvm: return SBI_ERR_FAILURE for pmu_event_info() when OOM commit 0e9d0e7a7c78db7aa1c13796c65cfe0aefa54a5b upstream. kvm_riscv_vcpu_pmu_event_info() returned -ENOMEM from the SBI extension handler, which caused kvm_riscv_vcpu_sbi_ecall() to abort KVM_RUN and surface the error to userspace instead of completing the ECALL with a negative SBI error in a0. Use SBI_ERR_FAILURE and the normal retdata path, matching other PMU handlers and kvm_sbi_ext_pmu_handler comment. Fixes: e309fd113b9f ("RISC-V: KVM: Implement get event info function") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Anup Patel Link: https://lore.kernel.org/r/20260514173642.41448-2-osama.abdelkader@gmail.com Signed-off-by: Anup Patel Signed-off-by: Greg Kroah-Hartman --- arch/riscv/kvm/vcpu_pmu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kvm/vcpu_pmu.c b/arch/riscv/kvm/vcpu_pmu.c index aeb16dd78035..ef55089c0c1a 100644 --- a/arch/riscv/kvm/vcpu_pmu.c +++ b/arch/riscv/kvm/vcpu_pmu.c @@ -478,8 +478,10 @@ int kvm_riscv_vcpu_pmu_event_info(struct kvm_vcpu *vcpu, unsigned long saddr_low } einfo = kzalloc(shmem_size, GFP_KERNEL); - if (!einfo) - return -ENOMEM; + if (!einfo) { + ret = SBI_ERR_FAILURE; + goto out; + } ret = kvm_vcpu_read_guest(vcpu, shmem, einfo, shmem_size); if (ret) { From bee400ad4f4259c9c0758e4f1960a1eed6f6f9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20L=C3=B3pez?= Date: Tue, 12 May 2026 12:00:42 +0200 Subject: [PATCH 112/697] virt: sev-guest: Explicitly leak pages in unknown state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fd948c3f96b18ff9ba7d3e8eae13d196593e1aaf upstream. When set_memory_{encrypted,decrypted}() fail, the user cannot know at which point the function failed, meaning that the pages are left in an unknown state from the point of view of the caller. Since the pages may be left in an unencrypted state, they are not suitable for general use, and cannot be returned safely to the buddy allocator. Avoid the issue by never freeing the pages, and then do the proper accounting by calling snp_leak_pages(). Fixes: 3e385c0d6ce8 ("virt: sev-guest: Move SNP Guest Request data pages handling under snp_cmd_mutex") Signed-off-by: Carlos López Signed-off-by: Borislav Petkov (AMD) Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/virt/coco/sev-guest/sev-guest.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/virt/coco/sev-guest/sev-guest.c b/drivers/virt/coco/sev-guest/sev-guest.c index 45b870b55feb..b6315cfe52bc 100644 --- a/drivers/virt/coco/sev-guest/sev-guest.c +++ b/drivers/virt/coco/sev-guest/sev-guest.c @@ -176,6 +176,7 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques struct snp_guest_req req = {}; int ret, npages = 0, resp_len; sockptr_t certs_address; + u64 pfn; if (sockptr_is_null(io->req_data) || sockptr_is_null(io->resp_data)) return -EINVAL; @@ -215,10 +216,11 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques if (!req.certs_data) return -ENOMEM; + pfn = PHYS_PFN(virt_to_phys(req.certs_data)); ret = set_memory_decrypted((unsigned long)req.certs_data, npages); if (ret) { pr_err("failed to mark page shared, ret=%d\n", ret); - free_pages_exact(req.certs_data, npages << PAGE_SHIFT); + snp_leak_pages(pfn, npages); return -EFAULT; } @@ -272,10 +274,12 @@ static int get_ext_report(struct snp_guest_dev *snp_dev, struct snp_guest_reques kfree(report_resp); e_free_data: if (npages) { - if (set_memory_encrypted((unsigned long)req.certs_data, npages)) + if (set_memory_encrypted((unsigned long)req.certs_data, npages)) { WARN_ONCE(ret, "failed to restore encryption mask (leak it)\n"); - else + snp_leak_pages(pfn, npages); + } else { free_pages_exact(req.certs_data, npages << PAGE_SHIFT); + } } return ret; } From ecdf21536c6d8ad079700fd3312225cd6b1e52bd Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 30 Apr 2026 21:49:42 +0200 Subject: [PATCH 113/697] drm/bridge: chipone-icn6211: use devm_drm_bridge_add in i2c probe commit 73d01051e8040c0b1de7fd26b3b8d0c2ffa6895c upstream. Use devm_drm_bridge_add() so the bridge is released if probe fails after registration, and drop drm_bridge_remove() in chipone_i2c_probe. Signed-off-by: Osama Abdelkader Fixes: 8dde6f7452a1 ("drm: bridge: icn6211: Add I2C configuration support") Cc: stable@vger.kernel.org Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20260430194944.78119-1-osama.abdelkader@gmail.com Signed-off-by: Luca Ceresoli Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/bridge/chipone-icn6211.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 814713c5bea9..553a1df4688d 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -758,7 +758,9 @@ static int chipone_i2c_probe(struct i2c_client *client) dev_set_drvdata(dev, icn); i2c_set_clientdata(client, icn); - drm_bridge_add(&icn->bridge); + ret = devm_drm_bridge_add(dev, &icn->bridge); + if (ret) + return ret; return chipone_dsi_host_attach(icn); } From 4bb4764f2c51f03f657a28029eb0595d8223aab5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 12 May 2026 09:43:34 +0200 Subject: [PATCH 114/697] spi: qup: fix error pointer deref after DMA setup failure commit a7e8f3efd50a165ba0189f6dc57f7e51a7d149db upstream. The driver falls back to PIO mode if DMA setup fails during probe. Make sure to the clear the DMA channel pointers on setup failure to avoid dereferencing an error pointer (or attempting to release a channel a second time) on later probe errors or driver unbind. This issue was flagged by Sashiko when reviewing a devres allocation conversion patch. Fixes: 612762e82ae6 ("spi: qup: Add DMA capabilities") Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=4 Cc: stable@vger.kernel.org # 4.1 Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260512074334.914735-1-johan@kernel.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-qup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index a5c549479c7d..3134b9c1480a 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -996,8 +996,11 @@ static int spi_qup_init_dma(struct spi_controller *host, resource_size_t base) err: dma_release_channel(host->dma_tx); + host->dma_tx = NULL; err_tx: dma_release_channel(host->dma_rx); + host->dma_rx = NULL; + return ret; } From a1f50f5aaa695417c0bc45636020e9310cc4ecc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Lebiedzi=C5=84ski?= Date: Mon, 6 Apr 2026 15:56:27 +0200 Subject: [PATCH 115/697] phy: exynos5-usbdrd: fix USB 2.0 HS PHY tuning values for Exynos7870 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5a759b120e31aa3ed914d98b51eb1755235250f2 upstream. The existing PHYPARAM0 tuning values for Exynos7870 are incorrect, causing the USB 2.0 PHY to fail high-speed negotiation and fall back to full-speed (12Mbps) operation. Fix TXVREFTUNE (transmitter voltage reference) from 14 to 3, TXRESTUNE (transmitter impedance) from 3 to 2, and SQRXTUNE (squelch threshold) from 6 to 5. Also explicitly set TXPREEMPPULSETUNE to 0, which was previously missing from the tuning table despite being included in the register mask. All values are derived from the vendor kernel for the Samsung Galaxy A6 (SM-A600FN), as no public hardware documentation is available for the Exynos7870 USB DRD PHY. With these corrections, the PHY successfully negotiates high-speed (480Mbps) operation. Fixes: 588d5d20ca8d ("phy: exynos5-usbdrd: add exynos7870 USBDRD support") Cc: stable@vger.kernel.org Tested-by: Kaustabh Chakraborty Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Lebiedziński Link: https://patch.msgid.link/20260406135627.234835-1-kernel@lvkasz.us Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 1c8bf80119f1..93ac302de1eb 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -1905,13 +1905,14 @@ const struct exynos5_usbdrd_phy_tuning exynos7870_tunes_utmi_postinit[] = { PHYPARAM0_TXPREEMPAMPTUNE | PHYPARAM0_TXHSXVTUNE | PHYPARAM0_TXFSLSTUNE | PHYPARAM0_SQRXTUNE | PHYPARAM0_OTGTUNE | PHYPARAM0_COMPDISTUNE), - (FIELD_PREP_CONST(PHYPARAM0_TXVREFTUNE, 14) | + (FIELD_PREP_CONST(PHYPARAM0_TXVREFTUNE, 3) | FIELD_PREP_CONST(PHYPARAM0_TXRISETUNE, 1) | - FIELD_PREP_CONST(PHYPARAM0_TXRESTUNE, 3) | + FIELD_PREP_CONST(PHYPARAM0_TXRESTUNE, 2) | + FIELD_PREP_CONST(PHYPARAM0_TXPREEMPPULSETUNE, 0) | FIELD_PREP_CONST(PHYPARAM0_TXPREEMPAMPTUNE, 0) | FIELD_PREP_CONST(PHYPARAM0_TXHSXVTUNE, 0) | FIELD_PREP_CONST(PHYPARAM0_TXFSLSTUNE, 3) | - FIELD_PREP_CONST(PHYPARAM0_SQRXTUNE, 6) | + FIELD_PREP_CONST(PHYPARAM0_SQRXTUNE, 5) | FIELD_PREP_CONST(PHYPARAM0_OTGTUNE, 2) | FIELD_PREP_CONST(PHYPARAM0_COMPDISTUNE, 3))), PHY_TUNING_ENTRY_LAST From 58f4a7bd8d73d3c27d7d3e88cc0b309fede40eb0 Mon Sep 17 00:00:00 2001 From: Wayne Chang Date: Mon, 4 May 2026 11:33:05 +0800 Subject: [PATCH 116/697] phy: tegra: xusb: Fix per-pad high-speed termination calibration commit da110228b54f2e2143d97ea7151e0dc22e539d67 upstream. The existing code reads a single hs_term_range_adj value from bit field [10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly. However, on SoCs that support per-pad termination, each pad has its own hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3 in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13] respectively. Fix the calibration by reading per-pad values from the appropriate fuse registers. For SoCs that do not support per-pad termination, replicate pad 0's value to all pads to maintain existing behavior. Add a has_per_pad_term flag to the SoC data to indicate whether per-pad termination values are available in FUSE_USB_CALIB_EXT_0. Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support") Cc: stable@vger.kernel.org Signed-off-by: Wayne Chang Signed-off-by: Wei-Cheng Chen Reviewed-by: Jon Hunter Tested-by: Jon Hunter Link: https://patch.msgid.link/20260504033305.2283145-1-weichengc@nvidia.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/tegra/xusb-tegra186.c | 33 ++++++++++++++++++++++++------- drivers/phy/tegra/xusb.h | 1 + 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/phy/tegra/xusb-tegra186.c b/drivers/phy/tegra/xusb-tegra186.c index bec9616c4a2e..4452e73fb82a 100644 --- a/drivers/phy/tegra/xusb-tegra186.c +++ b/drivers/phy/tegra/xusb-tegra186.c @@ -20,8 +20,8 @@ /* FUSE USB_CALIB registers */ #define HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? (11 + (x - 1) * 6) : 0) #define HS_CURR_LEVEL_PAD_MASK 0x3f -#define HS_TERM_RANGE_ADJ_SHIFT 7 -#define HS_TERM_RANGE_ADJ_MASK 0xf +#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x) ((x) ? (5 + (x - 1) * 4) : 7) +#define HS_TERM_RANGE_ADJ_PAD_MASK 0xf #define HS_SQUELCH_SHIFT 29 #define HS_SQUELCH_MASK 0x7 @@ -253,7 +253,7 @@ struct tegra_xusb_fuse_calibration { u32 *hs_curr_level; u32 hs_squelch; - u32 hs_term_range_adj; + u32 *hs_term_range_adj; u32 rpd_ctrl; }; @@ -930,7 +930,7 @@ static int tegra186_utmi_phy_power_on(struct phy *phy) value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); value &= ~TERM_RANGE_ADJ(~0); - value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj); + value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]); value &= ~RPD_CTRL(~0); value |= RPD_CTRL(priv->calib.rpd_ctrl); padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); @@ -1464,17 +1464,23 @@ static const char * const tegra186_usb3_functions[] = { static int tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) { + const struct tegra_xusb_padctl_soc *soc = padctl->base.soc; struct device *dev = padctl->base.dev; unsigned int i, count; u32 value, *level; + u32 *hs_term_range_adj; int err; - count = padctl->base.soc->ports.usb2.count; + count = soc->ports.usb2.count; level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL); if (!level) return -ENOMEM; + hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL); + if (!hs_term_range_adj) + return -ENOMEM; + err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value); if (err) return dev_err_probe(dev, err, @@ -1490,8 +1496,8 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) & HS_SQUELCH_MASK; - padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) & - HS_TERM_RANGE_ADJ_MASK; + hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) & + HS_TERM_RANGE_ADJ_PAD_MASK; err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value); if (err) { @@ -1503,6 +1509,17 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK; + for (i = 1; i < count; i++) { + if (soc->has_per_pad_term) + hs_term_range_adj[i] = + (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) & + HS_TERM_RANGE_ADJ_PAD_MASK; + else + hs_term_range_adj[i] = hs_term_range_adj[0]; + } + + padctl->calib.hs_term_range_adj = hs_term_range_adj; + return 0; } @@ -1708,6 +1725,7 @@ const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = { .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names), .supports_gen2 = true, .poll_trk_completed = true, + .has_per_pad_term = true, }; EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc); @@ -1732,6 +1750,7 @@ const struct tegra_xusb_padctl_soc tegra234_xusb_padctl_soc = { .trk_hw_mode = false, .trk_update_on_idle = true, .supports_lp_cfg_en = true, + .has_per_pad_term = true, }; EXPORT_SYMBOL_GPL(tegra234_xusb_padctl_soc); #endif diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h index d2b5f9565132..810b410672f3 100644 --- a/drivers/phy/tegra/xusb.h +++ b/drivers/phy/tegra/xusb.h @@ -436,6 +436,7 @@ struct tegra_xusb_padctl_soc { bool trk_hw_mode; bool trk_update_on_idle; bool supports_lp_cfg_en; + bool has_per_pad_term; }; struct tegra_xusb_padctl { From 78a369a065f1fa821b50730877d88aeaf9459974 Mon Sep 17 00:00:00 2001 From: Nitin Rawat Date: Wed, 15 Apr 2026 16:18:51 +0530 Subject: [PATCH 117/697] phy: qcom-qmp-ufs: Fix kaanapali PHY PLL lock failure after SM8650 G4 fix commit 80305760d7a55b884fb9023c490b75568d1ea0b1 upstream. Commit 81af9e40e2e4 ("phy: qcom: qmp-ufs: Fix SM8650 PCS table for Gear 4") moved QPHY_V6_PCS_UFS_PLL_CNTL register configuration from the shared sm8650_ufsphy_g5_pcs table to the SM8650-specific sm8650_ufsphy_pcs base table to fix Gear 4 operation on SM8650. However, this change inadvertently broke kaanapali and SM8750 SoCs which also rely on the shared sm8650_ufsphy_g5_pcs table for Gear 5 configuration but use their own sm8750_ufsphy_pcs base table. After the change, kaanapali PHYs are left without the required PLL_CNTL = 0x33 setting, causing the PHY PLL to remain at its hardware reset default value, preventing PLL lock and resulting in DME_LINKSTARTUP timeouts. Fix this by adding the missing QPHY_V6_PCS_UFS_PLL_CNTL = 0x33 entry to the sm8750_ufsphy_pcs table, mirroring what the original commit already did for sm8650_ufsphy_pcs. Cc: stable@vger.kernel.org # v6.19.12 Fixes: 81af9e40e2e4 ("phy: qcom: qmp-ufs: Fix SM8650 PCS table for Gear 4") Signed-off-by: Nitin Rawat Reviewed-by: Abel Vesa Reviewed-by: Konrad Dybcio Reviewed-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260415104851.2763238-1-nitin.rawat@oss.qualcomm.com Signed-off-by: Vinod Koul Signed-off-by: Greg Kroah-Hartman --- drivers/phy/qualcomm/phy-qcom-qmp-ufs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index dda877561f8c..8af777f7ddd0 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -1050,6 +1050,7 @@ static const struct qmp_phy_init_tbl sm8750_ufsphy_pcs[] = { QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_MULTI_LANE_CTRL1, 0x02), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_MID_TERM_CTRL1, 0x43), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PCS_CTRL1, 0x40), + QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_PLL_CNTL, 0x33), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0f), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_RX_SIGDET_CTRL2, 0x68), QMP_PHY_INIT_CFG(QPHY_V6_PCS_UFS_TX_POST_EMP_LVL_S4, 0x0e), From b9ff8631006233ba246828ac70409d2cb2da38d3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 17:04:20 -0400 Subject: [PATCH 118/697] scsi: isci: Fix use-after-free in device removal path commit b52a8d52c3125ec9a93106ed816582368de34426 upstream. The ISCI completion tasklet is initialized in isci_host_alloc() (drivers/scsi/isci/init.c:496) and scheduled from both MSI-X and legacy interrupt handlers (drivers/scsi/isci/host.c:223,613). isci_host_deinit() stops the controller and waits for stop completion, but it never kills completion_tasklet before teardown continues. A top-of-function tasklet_kill() is not sufficient here: interrupts are only disabled when isci_host_stop_complete() runs, so until wait_for_stop() returns the IRQ handlers can still requeue the tasklet. The tasklet callback also re-enables interrupts after draining completions, so killing the tasklet before the source is quiesced leaves the same race open. Once wait_for_stop() returns, no further IRQ-driven scheduling can occur. Kill completion_tasklet there so teardown cannot race a queued tasklet running on a dead ihost. On remove or unload, the stale callback can otherwise dereference ihost and touch ihost->smu_registers after the host lifetime ends. A UML + KASAN analogue reproduced the failure class both with no tasklet_kill() and with tasklet_kill() placed before source quiesce, and stayed clean once the kill happened after quiescing the scheduling source. This mirrors commit f6ab594672d4 ("scsi: aic94xx: fix use-after-free in device removal path"), but ISCI needs the kill after wait_for_stop(). Fixes: 6f231dda6808 ("isci: Intel(R) C600 Series Chipset Storage Control Unit Driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260419210420.2134639-1-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/isci/host.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index 6d2f4c831df7..ff199bab5d1a 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c @@ -1252,6 +1252,9 @@ void isci_host_deinit(struct isci_host *ihost) wait_for_stop(ihost); + /* No further IRQ-driven scheduling can happen past wait_for_stop(). */ + tasklet_kill(&ihost->completion_tasklet); + /* phy stop is after controller stop to allow port and device to * go idle before shutting down the phys, but the expectation is * that i/o has been shut off well before we reach this From 8e027db9fa310b1d5e7ad928510be800c4f004d9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 12 May 2026 09:48:49 +0200 Subject: [PATCH 119/697] spi: ep93xx: fix error pointer deref after DMA setup failure commit 5e121a81667a83e9a01d62b429e340f5a4a84abc upstream. The driver falls back to PIO mode if DMA setup fails during probe. Make sure to the clear the DMA channel pointers on setup failure to avoid dereferencing an error pointer on later probe errors or driver unbind. This issue was flagged by Sashiko when reviewing a devres allocation conversion patch. Fixes: e79e7c2df627 ("spi: ep93xx: add DT support for Cirrus EP93xx") Link: https://sashiko.dev/#/patchset/20260429091333.165363-1-johan%40kernel.org?part=10 Cc: stable@vger.kernel.org # 6.12 Cc: Nikita Shubin Signed-off-by: Johan Hovold Acked-by: Nikita Shubin Link: https://patch.msgid.link/20260512074849.915143-1-johan@kernel.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-ep93xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index e1d097091925..52d2f8920cd7 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -582,12 +582,14 @@ static int ep93xx_spi_setup_dma(struct device *dev, struct ep93xx_spi *espi) espi->dma_rx = dma_request_chan(dev, "rx"); if (IS_ERR(espi->dma_rx)) { ret = dev_err_probe(dev, PTR_ERR(espi->dma_rx), "rx DMA setup failed"); + espi->dma_rx = NULL; goto fail_free_page; } espi->dma_tx = dma_request_chan(dev, "tx"); if (IS_ERR(espi->dma_tx)) { ret = dev_err_probe(dev, PTR_ERR(espi->dma_tx), "tx DMA setup failed"); + espi->dma_tx = NULL; goto fail_release_rx; } From be409d2bbe9ca7da7b05cc7dde7499bc481f0766 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 12 May 2026 09:47:33 +0200 Subject: [PATCH 120/697] spi: sprd: fix error pointer deref after DMA setup failure commit 3d67fffb74267772d461c02c67f1eff893ad547d upstream. The driver falls back to PIO mode if DMA setup fails during probe. Make sure to check the dma.enabled flag before trying to release the DMA channels also on late probe errors to avoid dereferencing an error pointer (or attempting to release a channel a second time). This issue was flagged by Sashiko when reviewing a devres allocation conversion patch. Fixes: 386119bc7be9 ("spi: sprd: spi: sprd: Add DMA mode support") Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=10 Cc: stable@vger.kernel.org # 5.1 Cc: Lanqing Liu Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260512074733.915029-1-johan@kernel.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-sprd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 218c38841f05..827a307c7c82 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -992,7 +992,8 @@ static int sprd_spi_probe(struct platform_device *pdev) disable_clk: clk_disable_unprepare(ss->clk); release_dma: - sprd_spi_dma_release(ss); + if (ss->dma.enable) + sprd_spi_dma_release(ss); free_controller: spi_controller_put(sctlr); From d7a076fb596c7b408ed6df74793a597990a6d860 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 12 May 2026 09:48:09 +0200 Subject: [PATCH 121/697] spi: ti-qspi: fix use-after-free after DMA setup failure commit ea6ec3343e05f7937a53eb6d7617b3abdb4abc19 upstream. The driver falls back to PIO mode if DMA setup fails during probe. Make sure to clear the DMA channel pointer also if buffer allocation fails to avoid passing a pointer to the released channel to the DMA engine (or trying to free the channel a second time on late probe errors or driver unbind). This issue was flagged by Sashiko when reviewing a devres allocation conversion patch. Fixes: c687c46e9e45 ("spi: spi-ti-qspi: Use bounce buffer if read buffer is not DMA'ble") Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=17 Cc: stable@vger.kernel.org # 4.12 Cc: Vignesh R Signed-off-by: Johan Hovold Link: https://patch.msgid.link/20260512074809.915084-1-johan@kernel.org Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-ti-qspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index ba54d222d0e0..28a867c164c4 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -868,6 +868,7 @@ static int ti_qspi_probe(struct platform_device *pdev) dev_err(qspi->dev, "dma_alloc_coherent failed, using PIO mode\n"); dma_release_channel(qspi->rx_chan); + qspi->rx_chan = NULL; goto no_dma; } host->dma_rx = qspi->rx_chan; From 1012896f4225e8f801ff3c1648023845b66dfb11 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 13 May 2026 13:53:24 -0400 Subject: [PATCH 122/697] RDMA/siw: Reject MPA FPDU length underflow before signed receive math commit 0ce1bc9e46ecabe84772bb561e373c0d9876d6f2 upstream. A malicious connected siw peer can send an iWARP FPDU whose MPA length field (c_hdr->mpa_len, 16 bit big-endian, peer-controlled) is smaller than the fixed DDP/RDMAP header for the announced opcode. Soft-iWARP parses the full header in siw_get_hdr() based on iwarp_pktinfo[opcode] .hdr_len, but never compares mpa_len against that header length. siw_tcp_rx_data() then derives srx->fpdu_part_rem = be16_to_cpu(mpa_len) - fpdu_part_rcvd + MPA_HDR_SIZE; where fpdu_part_rcvd equals iwarp_pktinfo[opcode].hdr_len at this point. For a tagged WRITE (hdr_len 16, MPA_HDR_SIZE 2) the smallest on-wire mpa_len of 0 yields fpdu_part_rem = -14, and any mpa_len below hdr_len - MPA_HDR_SIZE underflows to a negative int. The signed value then flows into siw_proc_write()/siw_proc_rresp() as bytes = min(srx->fpdu_part_rem, srx->skb_new); is handed to siw_check_mem() as an int len (whose interval check addr + len > mem->va + mem->len is satisfied for a valid base when len is negative), and reaches siw_rx_data() -> siw_rx_kva() / siw_rx_umem() -> skb_copy_bits() as a signed copy length. The header copy branch in skb_copy_bits() promotes that to size_t, producing a multi-gigabyte read. KASAN under a KUnit harness that drives the real kernel TCP receive path -- a loopback AF_INET socketpair, the malformed FPDU written via kernel_sendmsg, sk_data_ready firing in softirq, tcp_read_sock dispatching to siw_tcp_rx_data -- reports: BUG: KASAN: use-after-free in skb_copy_bits+0x284/0x480 Read of size 4294967295 at addr ffff888... Call Trace: skb_copy_bits siw_rx_kva siw_rx_data siw_check_mem siw_proc_write siw_tcp_rx_data __tcp_read_sock siw_qp_llp_data_ready tcp_data_ready tcp_data_queue Add the missing invariant at the earliest point where the peer header is fully assembled. iwarp_pktinfo[*].hdr_len - MPA_HDR_SIZE is exactly the value the siw transmitter uses as the minimum mpa_len for each opcode (drivers/infiniband/sw/siw/siw_qp.c:33), so this matches the protocol contract. Out-of-range FPDUs terminate the connection with TERM_ERROR_LAYER_LLP / LLP_ETYPE_MPA / LLP_ECODE_FPDU_START -- which is RFC 5044 Section 8 error code 3 ("Marker and ULPDU Length fields do not agree on the start of an FPDU"), the correct framing-error class for this inconsistency. Fixes: 8b6a361b8c48 ("rdma/siw: receive path") Link: https://patch.msgid.link/r/20260513175325.2042630-2-michael.bommarito@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Acked-by: Bernard Metzler Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/sw/siw/siw_qp_rx.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/infiniband/sw/siw/siw_qp_rx.c b/drivers/infiniband/sw/siw/siw_qp_rx.c index e8a88b378d51..34d03584160c 100644 --- a/drivers/infiniband/sw/siw/siw_qp_rx.c +++ b/drivers/infiniband/sw/siw/siw_qp_rx.c @@ -1081,6 +1081,21 @@ static int siw_get_hdr(struct siw_rx_stream *srx) return -EAGAIN; } + /* + * Peer-controlled mpa_len must not underflow srx->fpdu_part_rem + * in siw_tcp_rx_data(); a negative value flows as a signed copy + * length into siw_check_mem() and skb_copy_bits(). + */ + if (unlikely(be16_to_cpu(c_hdr->mpa_len) + MPA_HDR_SIZE < + iwarp_pktinfo[opcode].hdr_len)) { + pr_warn_ratelimited("siw: short mpa_len %u for opcode %u (hdr_len %u)\n", + be16_to_cpu(c_hdr->mpa_len), opcode, + iwarp_pktinfo[opcode].hdr_len); + siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_LLP, + LLP_ETYPE_MPA, LLP_ECODE_FPDU_START, 0); + return -EINVAL; + } + /* * DDP/RDMAP header receive completed. Check if the current * DDP segment starts a new RDMAP message or continues a previously From 9e3f18883a98420a3b8873c6f894bc57e9b98e41 Mon Sep 17 00:00:00 2001 From: Heechan Kang Date: Sun, 17 May 2026 15:22:32 +0900 Subject: [PATCH 123/697] fwctl: pds: Validate RPC input size before parsing commit e7537735028c3ad4b0bfc02ff8fa2a1a28aa04fe upstream. The fwctl core allocates the device-specific RPC input buffer with fwctl_rpc.in_len and passes that buffer to the driver callback. pdsfc_fw_rpc() casts the buffer to struct fwctl_rpc_pds and then calls pdsfc_validate_rpc(), which reads fields from that structure before checking that the input buffer is large enough to contain it. A short in_len can make pds_fwctl read beyond the allocation. Reject pds RPC buffers that are smaller than struct fwctl_rpc_pds before parsing any pds-specific fields. Fixes: 92c66ee829b9 ("pds_fwctl: add rpc and query support") Link: https://patch.msgid.link/r/20260517062232.1858747-1-gganji11@naver.com Cc: stable@vger.kernel.org # v6.15+ Signed-off-by: Heechan Kang Reviewed-by: Dave Jiang Signed-off-by: Jason Gunthorpe Signed-off-by: Greg Kroah-Hartman --- drivers/fwctl/pds/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/fwctl/pds/main.c b/drivers/fwctl/pds/main.c index 1809853f6353..ccc5654d3ae7 100644 --- a/drivers/fwctl/pds/main.c +++ b/drivers/fwctl/pds/main.c @@ -362,6 +362,9 @@ static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, void *out = NULL; int err; + if (in_len < sizeof(*rpc)) + return ERR_PTR(-EINVAL); + err = pdsfc_validate_rpc(pdsfc, rpc, scope); if (err) return ERR_PTR(err); From f27a3b9aadfb53150a1b033f1d463f63ad800cf6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 22 May 2026 15:05:07 +0800 Subject: [PATCH 124/697] LoongArch: kprobes: Use larch_insn_text_copy() to patch instructions commit e3ef9a28f558d1cbf0b42d6dcd16c60da557562b upstream. On SMP systems, kprobe handlers would occasionally fail to execute on certain CPU cores. The issue is hard to reproduce and typically occurs randomly under high system load. The root cause is a software-side instruction hazard. According to the LoongArch Reference Manual, while the cache coherency is maintained by hardware, software must explicitly use the "IBAR" instruction to ensure the instruction fetch unit (IFU) observes the effects of recent stores. The current arch_arm_kprobe() and arch_disarm_kprobe() only execute the "IBAR" barrier (via flush_insn_slot -> local_flush_icache_range) on the local CPU. This leaves a vulnerable window where remote CPU cores may continue executing stale instructions from their pipelines or prefetch buffers, as they have not executed an "IBAR" since the code modification. Switch to larch_insn_text_copy() to fix this: 1. Synchronization: It uses stop_machine_cpuslocked() to synchronize all online CPUs, ensuring no CPU is executing the target code area during modification. 2. Visibility: By passing cpu_online_mask to stop_machine_cpuslocked(), the callback text_copy_cb() is executed on all online cores. Each CPU core invokes local_flush_icache_range() to execute "IBAR", clearing instruction hazards system-wide and ensuring the "break" instruction is visible to the fetch units of all cores. 3. Robustness: It properly manages memory write permissions (ROX/RW) for the kernel text segment during patching, ensuring compatibility with CONFIG_STRICT_KERNEL_RWX. Cc: # 6.18+ Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/kernel/kprobes.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c index 8ba391cfabb0..04b5b05715cd 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -60,16 +60,18 @@ NOKPROBE_SYMBOL(arch_prepare_kprobe); /* Install breakpoint in text */ void arch_arm_kprobe(struct kprobe *p) { - *p->addr = KPROBE_BP_INSN; - flush_insn_slot(p); + u32 insn = KPROBE_BP_INSN; + + larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE); } NOKPROBE_SYMBOL(arch_arm_kprobe); /* Remove breakpoint from text */ void arch_disarm_kprobe(struct kprobe *p) { - *p->addr = p->opcode; - flush_insn_slot(p); + u32 insn = p->opcode; + + larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE); } NOKPROBE_SYMBOL(arch_disarm_kprobe); From 22d9b9739b8ead1445ad4b3aa514b181b29b6893 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 21 May 2026 20:58:40 +0800 Subject: [PATCH 125/697] LoongArch: Remove unused code to avoid build warning commit 0ccc9d47cf020994097ff51827cebd04aa2b0bf4 upstream. After commit feee6b2989165631b1 ("mm/memory_hotplug: shrink zones when offlining memory"), __remove_pages() doesn't need the "zone" parameter so the "page" variable is also unused. Remove the unused code to avoid such build warning: arch/loongarch/mm/init.c: In function 'arch_remove_memory': arch/loongarch/mm/init.c:134:22: warning: variable 'page' set but not used [-Wunused-but-set-variable=] 134 | struct page *page = pfn_to_page(start_pfn); Cc: Reviewed-by: Guo Ren Signed-off-by: Huacai Chen Signed-off-by: Greg Kroah-Hartman --- arch/loongarch/mm/init.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c index 6bfd4b8dad1b..900ce1da75a4 100644 --- a/arch/loongarch/mm/init.c +++ b/arch/loongarch/mm/init.c @@ -97,11 +97,7 @@ void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) { unsigned long start_pfn = start >> PAGE_SHIFT; unsigned long nr_pages = size >> PAGE_SHIFT; - struct page *page = pfn_to_page(start_pfn); - /* With altmap the first mapped page is offset from @start */ - if (altmap) - page += vmem_altmap_offset(altmap); __remove_pages(start_pfn, nr_pages, altmap); } #endif From 508fd8ab158abd04b7f7d0f707cd6d6c405df4ea Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 6 May 2026 13:57:00 +0200 Subject: [PATCH 126/697] device property: set fwnode->secondary to NULL in fwnode_init() commit 215c90ee656114f5e8c32408228d97082f8e0eef upstream. If a firmware node is allocated on the stack (for instance: temporary software node whose life-time we control) or on the heap - but using a non-zeroing allocation function - and initialized using fwnode_init(), its secondary pointer will contain uninitalized memory which likely will be neither NULL nor IS_ERR() and so may end up being dereferenced (for example: in dev_to_swnode()). Set fwnode->secondary to NULL on initialization. Cc: stable Fixes: 01bb86b380a3 ("driver core: Add fwnode_init()") Signed-off-by: Bartosz Golaszewski Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Reviewed-by: Sakari Ailus Link: https://patch.msgid.link/20260506115701.23035-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 80b38fbf2121..31df7608737e 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -208,6 +208,7 @@ struct fwnode_operations { static inline void fwnode_init(struct fwnode_handle *fwnode, const struct fwnode_operations *ops) { + fwnode->secondary = NULL; fwnode->ops = ops; INIT_LIST_HEAD(&fwnode->consumers); INIT_LIST_HEAD(&fwnode->suppliers); From 942968260e61d4a5d7552b20814b6277f9c553df Mon Sep 17 00:00:00 2001 From: Daniel J Blueman Date: Fri, 8 May 2026 14:57:21 +0800 Subject: [PATCH 127/697] drm/msm: Fix shrinker deadlock commit 3392291fc509d8ad6e4ad90f15b0a193f721cbc9 upstream. With PROVE_LOCKING on an Snapdragon X1 and VM reclaim pressure, we see: ====================================================== WARNING: possible circular locking dependency detected 7.0.0-debug+ #43 Tainted: G W ------------------------------------------------------ kswapd0/82 is trying to acquire lock: ffff800080ec3870 (reservation_ww_class_acquire){+.+.}-{0:0}, at: msm_gem_shrinker_scan+0x17c/0x400 [msm] but task is already holding lock: ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (fs_reclaim){+.+.}-{0:0}: __lock_acquire+0x4d0/0xad0 lock_acquire.part.0+0xc4/0x248 lock_acquire+0x8c/0x248 fs_reclaim_acquire+0xd0/0xf0 dma_resv_lockdep+0x224/0x348 do_one_initcall+0x84/0x5d0 do_initcalls+0x194/0x1d8 kernel_init_freeable+0x128/0x180 kernel_init+0x2c/0x160 ret_from_fork+0x10/0x20 -> #1 (reservation_ww_class_mutex){+.+.}-{4:4}: __lock_acquire+0x4d0/0xad0 lock_acquire.part.0+0xc4/0x248 lock_acquire+0x8c/0x248 dma_resv_lockdep+0x1a8/0x348 do_one_initcall+0x84/0x5d0 do_initcalls+0x194/0x1d8 kernel_init_freeable+0x128/0x180 kernel_init+0x2c/0x160 ret_from_fork+0x10/0x20 -> #0 (reservation_ww_class_acquire){+.+.}-{0:0}: check_prev_add+0x114/0x790 validate_chain+0x594/0x6f0 __lock_acquire+0x4d0/0xad0 lock_acquire.part.0+0xc4/0x248 lock_acquire+0x8c/0x248 drm_gem_lru_scan+0x1ac/0x440 msm_gem_shrinker_scan+0x17c/0x400 [msm] do_shrink_slab+0x150/0x4a0 shrink_slab+0x144/0x460 shrink_one+0x9c/0x1b0 shrink_many+0x27c/0x5c0 shrink_node+0x344/0x550 balance_pgdat+0x2c0/0x988 kswapd+0x11c/0x318 kthread+0x10c/0x128 ret_from_fork+0x10/0x20 other info that might help us debug this: Chain exists of: reservation_ww_class_acquire --> reservation_ww_class_mutex --> fs_reclaim Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(fs_reclaim); lock(reservation_ww_class_mutex); lock(fs_reclaim); lock(reservation_ww_class_acquire); *** DEADLOCK *** 1 lock held by kswapd0/82: #0: ffffc31709b263b8 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x88/0x988 stack backtrace: CPU: 4 UID: 0 PID: 82 Comm: kswapd0 Tainted: G W 7.0.0-debug+ #43 PREEMPT(full) Tainted: [W]=WARN Hardware name: LENOVO 21BX0016US/21BX0016US, BIOS N3HET94W (1.66 ) 09/15/2025 Call trace: show_stack+0x20/0x40 (C) dump_stack_lvl+0x9c/0xd0 dump_stack+0x18/0x30 print_circular_bug+0x114/0x120 check_noncircular+0x178/0x198 check_prev_add+0x114/0x790 validate_chain+0x594/0x6f0 __lock_acquire+0x4d0/0xad0 lock_acquire.part.0+0xc4/0x248 lock_acquire+0x8c/0x248 drm_gem_lru_scan+0x1ac/0x440 msm_gem_shrinker_scan+0x17c/0x400 [msm] do_shrink_slab+0x150/0x4a0 shrink_slab+0x144/0x460 shrink_one+0x9c/0x1b0 shrink_many+0x27c/0x5c0 shrink_node+0x344/0x550 balance_pgdat+0x2c0/0x988 kswapd+0x11c/0x318 kthread+0x10c/0x128 ret_from_fork+0x10/0x20 kswapd0 holding fs_reclaim calls the MSM shrinker, which calls dma_resv_lock. This in turn acquires fs_reclaim. Fix this deadlock by using dma_resv_trylock() instead, dropping the subsequently unused passed wait-wound lock 'ticket'. Cc: stable@vger.kernel.org Signed-off-by: Daniel J Blueman Fixes: fe4952b5f27c ("drm/msm: Convert vm locking") Patchwork: https://patchwork.freedesktop.org/patch/723564/ Message-ID: <20260508065722.18785-1-daniel@quora.org> [rob: fixup compile errors, replace lockdep splat with something legible] Signed-off-by: Rob Clark Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/msm/msm_gem_shrinker.c | 40 +++++++++++--------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index 31fa51a44f86..6e39e4e578bb 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -43,8 +43,7 @@ msm_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc) } static bool -with_vm_locks(struct ww_acquire_ctx *ticket, - void (*fn)(struct drm_gem_object *obj), +with_vm_locks(void (*fn)(struct drm_gem_object *obj), struct drm_gem_object *obj) { /* @@ -52,7 +51,7 @@ with_vm_locks(struct ww_acquire_ctx *ticket, * success paths */ struct drm_gpuvm_bo *vm_bo, *last_locked = NULL; - int ret = 0; + bool locked = true; drm_gem_for_each_gpuvm_bo (vm_bo, obj) { struct dma_resv *resv = drm_gpuvm_resv(vm_bo->vm); @@ -60,23 +59,14 @@ with_vm_locks(struct ww_acquire_ctx *ticket, if (resv == obj->resv) continue; - ret = dma_resv_lock(resv, ticket); - - /* - * Since we already skip the case when the VM and obj - * share a resv (ie. _NO_SHARE objs), we don't expect - * to hit a double-locking scenario... which the lock - * unwinding cannot really cope with. - */ - WARN_ON(ret == -EALREADY); - /* - * Don't bother with slow-lock / backoff / retry sequence, - * if we can't get the lock just give up and move on to - * the next object. + * dma_resv_lock can't be used due to acquiring 'ticket' before the + * fs_reclaim lock, which is held in shrinker context */ - if (ret) + if (!dma_resv_trylock(resv)) { + locked = false; goto out_unlock; + } /* * Hold a ref to prevent the vm_bo from being freed @@ -108,11 +98,11 @@ with_vm_locks(struct ww_acquire_ctx *ticket, } } - return ret == 0; + return locked; } static bool -purge(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket) +purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) { if (!is_purgeable(to_msm_bo(obj))) return false; @@ -120,11 +110,11 @@ purge(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket) if (msm_gem_active(obj)) return false; - return with_vm_locks(ticket, msm_gem_purge, obj); + return with_vm_locks(msm_gem_purge, obj); } static bool -evict(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket) +evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) { if (is_unevictable(to_msm_bo(obj))) return false; @@ -132,7 +122,7 @@ evict(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket) if (msm_gem_active(obj)) return false; - return with_vm_locks(ticket, msm_gem_evict, obj); + return with_vm_locks(msm_gem_evict, obj); } static bool @@ -164,7 +154,6 @@ static unsigned long msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) { struct msm_drm_private *priv = shrinker->private_data; - struct ww_acquire_ctx ticket; struct { struct drm_gem_lru *lru; bool (*shrink)(struct drm_gem_object *obj, struct ww_acquire_ctx *ticket); @@ -185,11 +174,14 @@ msm_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) for (unsigned i = 0; (nr > 0) && (i < ARRAY_SIZE(stages)); i++) { if (!stages[i].cond) continue; + /* + * 'ticket' not needed on trylock paths + */ stages[i].freed = drm_gem_lru_scan(stages[i].lru, nr, &stages[i].remaining, stages[i].shrink, - &ticket); + NULL); nr -= stages[i].freed; freed += stages[i].freed; remaining += stages[i].remaining; From 0f8efc45740b0628a787d1b0be8a0ddabd700625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Fri, 15 May 2026 12:07:14 -0300 Subject: [PATCH 128/697] drm/v3d: Fix use-after-free of CPU job query arrays on error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b0fe80c0b9250b35e2211bf3117e7aca814a21b0 upstream. The CPU job ioctl's fail label calls kvfree() on cpu_job's timestamp and performance query arrays after v3d_job_cleanup(), which drops the job's last reference and frees cpu_job. Reading cpu_job at that point is a use-after-free. Also, on the early v3d_job_init() failure path, it is a NULL dereference, since v3d_job_deallocate() zeroes the local pointer. In the success path, the arrays are released from the scheduler's .free_job callback, but on the error path, they are freed manually, as the job was never pushed to the scheduler. While the success path deals with this correctly, the fail path doesn't. On top of that, the manual kvfree() calls only free the array storage; they don't drm_syncobj_put() the per-query syncobjs that v3d_timestamp_query_info_free() and v3d_performance_query_info_free() release on the success path. So the same fail path that triggers the use-after-free also leaks one syncobj reference per query. Unify the CPU job teardown into the CPU job's kref destructor, mirroring v3d_render_job_free(). The scheduler's .free_job slot reverts to the generic v3d_sched_job_free() and the fail label drops the manual kvfree() calls, leaving a single teardown path that is reached from both the scheduler and the ioctl error path. That removes the use-after-free, the NULL dereference, and the syncobj leak by construction. Cc: stable@vger.kernel.org Fixes: 9ba0ff3e083f ("drm/v3d: Create a CPU job extension for the timestamp query job") Assisted-by: Claude:claude-opus-4.7 Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260515-v3d-cpu-job-leaks-v1-1-7f147cbbf935@igalia.com Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/v3d/v3d_sched.c | 16 +--------------- drivers/gpu/drm/v3d/v3d_submit.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 0ec06bfbbebb..e0cbd12c51c9 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -103,20 +103,6 @@ v3d_performance_query_info_free(struct v3d_performance_query_info *query_info, } } -static void -v3d_cpu_job_free(struct drm_sched_job *sched_job) -{ - struct v3d_cpu_job *job = to_cpu_job(sched_job); - - v3d_timestamp_query_info_free(&job->timestamp_query, - job->timestamp_query.count); - - v3d_performance_query_info_free(&job->performance_query, - job->performance_query.count); - - v3d_job_cleanup(&job->base); -} - static void v3d_switch_perfmon(struct v3d_dev *v3d, struct v3d_job *job) { @@ -860,7 +846,7 @@ static const struct drm_sched_backend_ops v3d_cache_clean_sched_ops = { static const struct drm_sched_backend_ops v3d_cpu_sched_ops = { .run_job = v3d_cpu_job_run, - .free_job = v3d_cpu_job_free + .free_job = v3d_sched_job_free }; static int diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 23fa18e5e65c..acb1dd7458a3 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -119,6 +119,21 @@ v3d_render_job_free(struct kref *ref) v3d_job_free(ref); } +static void +v3d_cpu_job_free(struct kref *ref) +{ + struct v3d_cpu_job *job = container_of(ref, struct v3d_cpu_job, + base.refcount); + + v3d_timestamp_query_info_free(&job->timestamp_query, + job->timestamp_query.count); + + v3d_performance_query_info_free(&job->performance_query, + job->performance_query.count); + + v3d_job_free(ref); +} + void v3d_job_cleanup(struct v3d_job *job) { if (!job) @@ -1321,7 +1336,7 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type); ret = v3d_job_init(v3d, file_priv, &cpu_job->base, - v3d_job_free, 0, &se, V3D_CPU); + v3d_cpu_job_free, 0, &se, V3D_CPU); if (ret) { v3d_job_deallocate((void *)&cpu_job); goto fail; @@ -1404,8 +1419,6 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, v3d_job_cleanup((void *)csd_job); v3d_job_cleanup(clean_job); v3d_put_multisync_post_deps(&se); - kvfree(cpu_job->timestamp_query.queries); - kvfree(cpu_job->performance_query.queries); return ret; } From 35671087a272cd236ff2785eff16970b0ee9b112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Fri, 15 May 2026 12:07:15 -0300 Subject: [PATCH 129/697] drm/v3d: Release indirect CSD GEM reference on CPU job free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6eb6e5acafa46854d4363e6c34981289995f3ace upstream. v3d_get_cpu_indirect_csd_params() takes a reference to the indirect BO via drm_gem_object_lookup() and stashes it in cpu_job->indirect_csd.indirect, but nothing on the CPU job teardown path ever drops that reference. Drop the extra reference in v3d_cpu_job_free(). The NULL check covers ioctl errors before the lookup ran and CPU job types other than V3D_CPU_JOB_TYPE_INDIRECT_CSD, which leave the field zero-initialised. Cc: stable@vger.kernel.org Fixes: 18b8413b25b7 ("drm/v3d: Create a CPU job extension for a indirect CSD job") Assisted-by: Claude:claude-opus-4.7 Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260515-v3d-cpu-job-leaks-v1-2-7f147cbbf935@igalia.com Signed-off-by: Maíra Canal Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/v3d/v3d_submit.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index acb1dd7458a3..95b9e68b0dfe 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -131,6 +131,9 @@ v3d_cpu_job_free(struct kref *ref) v3d_performance_query_info_free(&job->performance_query, job->performance_query.count); + if (job->indirect_csd.indirect) + drm_gem_object_put(job->indirect_csd.indirect); + v3d_job_free(ref); } From 8fadd01cf461fee5bb11506621339c548447e5c7 Mon Sep 17 00:00:00 2001 From: Deepanshu Kartikey Date: Tue, 19 May 2026 13:52:47 +0530 Subject: [PATCH 130/697] drm/virtio: use uninterruptible resv lock for plane updates commit 9af1b6e175c82daf4b423da339a722d8e67a735a upstream. virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush() lock the framebuffer BO's dma_resv via virtio_gpu_array_lock_resv() and ignore its return value. The function can fail with -EINTR from dma_resv_lock_interruptible() (signal during lock wait) or with -ENOMEM from dma_resv_reserve_fences() (fence slot allocation), leaving the resv lock not held. The queue path then walks the object array and calls dma_resv_add_fence(), which requires the lock held; with lockdep enabled this trips dma_resv_assert_held(): WARNING: drivers/dma-buf/dma-resv.c:296 at dma_resv_add_fence+0x71e/0x840 Call Trace: virtio_gpu_array_add_fence virtio_gpu_queue_ctrl_sgs virtio_gpu_queue_fenced_ctrl_buffer virtio_gpu_cursor_plane_update drm_atomic_helper_commit_planes drm_atomic_helper_commit_tail commit_tail drm_atomic_helper_commit drm_atomic_commit drm_atomic_helper_update_plane __setplane_atomic drm_mode_cursor_universal drm_mode_cursor_common drm_mode_cursor_ioctl drm_ioctl __x64_sys_ioctl Beyond the WARN, mutating the dma_resv fence list without the lock races with concurrent readers/writers and can corrupt the list. Both call sites run inside the .atomic_update plane callback, which DRM atomic helpers do not allow to fail (by the time it runs, the commit has been signed off to userspace and there is no clean rollback path). Moving the lock acquisition to .prepare_fb was rejected because the broader lock scope deadlocks against other BO locking paths in the same atomic commit. Introduce virtio_gpu_lock_one_resv_uninterruptible() that uses dma_resv_lock() instead of dma_resv_lock_interruptible(). This eliminates the -EINTR failure mode -- the realistic syzbot trigger -- without extending the lock hold across the commit. The helper locks a single BO and rejects nents > 1 with -EINVAL; both fix sites lock exactly one BO. Use it from virtio_gpu_cursor_plane_update() and virtio_gpu_resource_flush(); check the return value to handle the remaining -ENOMEM case from dma_resv_reserve_fences() by freeing the objs and skipping the plane update for that frame. The framebuffer BOs touched here are not shared with other contexts and lock contention is expected to be brief, so the loss of signal-interruptibility is acceptable. Other callers of virtio_gpu_array_lock_resv() (the ioctl paths) continue to use the interruptible variant. The bug was reported by syzbot, triggered via fault injection (fail_nth) on the DRM_IOCTL_MODE_CURSOR path, which forces the -ENOMEM branch in dma_resv_reserve_fences(). Reported-by: syzbot+72bd3dd3a5d5f39a0271@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=72bd3dd3a5d5f39a0271 Fixes: 5cfd31c5b3a3 ("drm/virtio: fix virtio_gpu_cursor_plane_update().") Cc: stable@vger.kernel.org Signed-off-by: Deepanshu Kartikey Signed-off-by: Dmitry Osipenko Link: https://patch.msgid.link/20260519082247.34470-1-kartikey406@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + drivers/gpu/drm/virtio/virtgpu_gem.c | 17 +++++++++++++++++ drivers/gpu/drm/virtio/virtgpu_plane.c | 10 ++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index f17660a71a3e..2f3531950aa4 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -317,6 +317,7 @@ virtio_gpu_array_from_handles(struct drm_file *drm_file, u32 *handles, u32 nents void virtio_gpu_array_add_obj(struct virtio_gpu_object_array *objs, struct drm_gem_object *obj); int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs); +int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs); void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs); void virtio_gpu_array_add_fence(struct virtio_gpu_object_array *objs, struct dma_fence *fence); diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 90c99d83c4cf..015b5debd745 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -238,6 +238,23 @@ int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs) return ret; } +int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs) +{ + int ret; + + if (objs->nents != 1) + return -EINVAL; + + dma_resv_lock(objs->objs[0]->resv, NULL); + + ret = dma_resv_reserve_fences(objs->objs[0]->resv, 1); + if (ret) { + virtio_gpu_array_unlock_resv(objs); + return ret; + } + return 0; +} + void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs) { if (objs->nents == 1) { diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 29e4b458ae57..192327723bb9 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -214,7 +214,10 @@ static void virtio_gpu_resource_flush(struct drm_plane *plane, if (!objs) return; virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]); - virtio_gpu_array_lock_resv(objs); + if (virtio_gpu_lock_one_resv_uninterruptible(objs)) { + virtio_gpu_array_put_free(objs); + return; + } virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y, width, height, objs, vgplane_st->fence); @@ -458,7 +461,10 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, if (!objs) return; virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]); - virtio_gpu_array_lock_resv(objs); + if (virtio_gpu_lock_one_resv_uninterruptible(objs)) { + virtio_gpu_array_put_free(objs); + return; + } virtio_gpu_cmd_transfer_to_host_2d (vgdev, 0, plane->state->crtc_w, From 3ed448c1dc78ddbf2e1f29dc00788c028ccdbb82 Mon Sep 17 00:00:00 2001 From: Alan Liu Date: Fri, 1 May 2026 12:35:48 +0800 Subject: [PATCH 131/697] drm/amdgpu/vpe: Force collaborate sync after TRAP commit b6074630a461b1322a814988779005cbc43612ea upstream. VPE1 could possibly hang and fail to power off at the end of commands in collaboration mode. This workaround adds a COLLAB_SYNC after TRAP to force instances synchronized to avoid VPE1 fail to power off. Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alan liu Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5171 Signed-off-by: Alex Deucher (cherry picked from commit a8b749c5c5afb7e5daa2bfb95d958fb3c6b8f055) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c index aa78c2ee9e21..bd5eff4bafe7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c @@ -562,6 +562,11 @@ static void vpe_ring_emit_fence(struct amdgpu_ring *ring, uint64_t addr, amdgpu_ring_write(ring, 0); } + /* WA: Force sync after TRAP to avoid VPE1 fail to power off */ + if (ring->adev->vpe.collaborate_mode) { + amdgpu_ring_write(ring, VPE_CMD_HEADER(VPE_CMD_OPCODE_COLLAB_SYNC, 0)); + amdgpu_ring_write(ring, 0xabcd); + } } static void vpe_ring_emit_pipeline_sync(struct amdgpu_ring *ring) @@ -968,7 +973,7 @@ static const struct amdgpu_ring_funcs vpe_ring_funcs = { .emit_frame_size = 5 + /* vpe_ring_init_cond_exec */ 6 + /* vpe_ring_emit_pipeline_sync */ - 10 + 10 + 10 + /* vpe_ring_emit_fence */ + 12 + 12 + 12 + /* vpe_ring_emit_fence */ /* vpe_ring_emit_vm_flush */ SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 + SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 6, From 95306db11956dd8d82f56a20452c13008519388a Mon Sep 17 00:00:00 2001 From: Julien Chauveau Date: Tue, 24 Mar 2026 20:30:11 +0100 Subject: [PATCH 132/697] drm/bridge: it66121: acquire reset GPIO in probe commit e02b5262fd288cc235f14e12233ea54e78c04611 upstream. The it66121_ctx structure has a gpio_reset field, and it66121_hw_reset() calls gpiod_set_value() on it. However, the GPIO descriptor is never acquired via devm_gpiod_get(), leaving gpio_reset as NULL throughout the driver lifetime. gpiod_set_value() silently returns when passed a NULL descriptor, so the hardware reset sequence in it66121_hw_reset() is a no-op. This leaves the chip in an undefined state at probe time, which can prevent it from responding on the I2C bus. The DT binding marks reset-gpios as a required property, so all compliant device trees provide this GPIO. Add the missing devm_gpiod_get() call after enabling power supplies and before the hardware reset, so the chip is properly reset with power applied. Fixes: 988156dc2fc9 ("drm: bridge: add it66121 driver") Cc: stable@vger.kernel.org Signed-off-by: Julien Chauveau Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Link: https://patch.msgid.link/20260324193011.16583-1-chauveau.julien@gmail.com Signed-off-by: Javier Martinez Canillas Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/bridge/ite-it66121.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index aa7b1dcc5d70..f9aff56dd503 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -1559,6 +1559,11 @@ static int it66121_probe(struct i2c_client *client) return ret; } + ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->gpio_reset)) + return dev_err_probe(dev, PTR_ERR(ctx->gpio_reset), + "Failed to get reset GPIO\n"); + it66121_hw_reset(ctx); ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); From d35563813296ec7e965ad87415a0f82944bc41a2 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 30 Apr 2026 21:56:59 +0200 Subject: [PATCH 133/697] drm/bridge: megachips: remove bridge when irq request fails commit d45d5c819f2cd0b6b5d76a194a537a5f4aeefecb upstream. If devm_request_threaded_irq() fails after drm_bridge_add(), remove the bridge before returning. Keep drm_bridge_add() rather than devm_drm_bridge_add(): registration is tied to the STDP4028 device while ge_b850v3_register() may complete from either I2C probe; devm would not unwind the bridge if the other client's probe fails. Signed-off-by: Osama Abdelkader Fixes: fcfa0ddc18ed ("drm/bridge: Drivers for megachips-stdpxxxx-ge-b850v3-fw (LVDS-DP++)") Cc: stable@vger.kernel.org Reviewed-by: Luca Ceresoli Tested-by: Ian Ray Link: https://patch.msgid.link/20260430195700.80317-1-osama.abdelkader@gmail.com Signed-off-by: Luca Ceresoli Signed-off-by: Greg Kroah-Hartman --- .../drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index c9e6505cbd88..2d02cc69f237 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -251,7 +251,6 @@ static void ge_b850v3_lvds_remove(void) goto out; drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge); - ge_b850v3_lvds_ptr = NULL; out: mutex_unlock(&ge_b850v3_lvds_dev_mutex); @@ -261,6 +260,7 @@ static int ge_b850v3_register(void) { struct i2c_client *stdp4028_i2c = ge_b850v3_lvds_ptr->stdp4028_i2c; struct device *dev = &stdp4028_i2c->dev; + int ret; /* drm bridge initialization */ ge_b850v3_lvds_ptr->bridge.ops = DRM_BRIDGE_OP_DETECT | @@ -277,11 +277,15 @@ static int ge_b850v3_register(void) if (!stdp4028_i2c->irq) return 0; - return devm_request_threaded_irq(&stdp4028_i2c->dev, - stdp4028_i2c->irq, NULL, - ge_b850v3_lvds_irq_handler, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); + ret = devm_request_threaded_irq(&stdp4028_i2c->dev, + stdp4028_i2c->irq, NULL, + ge_b850v3_lvds_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); + if (ret) + drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge); + + return ret; } static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c) From 6bbd703ea1c141d7ac0e7f7e82ff5fd237b67a17 Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 4 May 2026 11:14:45 -0400 Subject: [PATCH 134/697] drm/amd/display: Fix integer overflow in bios_get_image() commit cd86529ec61474a38c3837fb7823790a7c3f8cce upstream. [Why&How] The bounds check in bios_get_image() computes 'offset + size' using unsigned 32-bit arithmetic before comparing against bios_size. If a VBIOS image contains a near-UINT32_MAX offset the addition wraps to a small value, the comparison passes, and the function returns a wild pointer past the VBIOS mapping. Additionally, the comparison uses '<' (strict), which incorrectly rejects the valid exact-fit case where offset + size == bios_size. Fix both issues by restructuring the check to avoid the addition entirely: first reject if offset alone exceeds bios_size, then check size against the remaining space (bios_size - offset). This eliminates the overflow and correctly permits exact-fit accesses. Assisted-by: GitHub Copilot:claude-opus-4.6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ivan Lipski Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit d40fb392af659c4a02b560319f226842f6ec1a95) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c index 8d2cf95ae739..e00dc05c2d9d 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c @@ -37,10 +37,13 @@ uint8_t *bios_get_image(struct dc_bios *bp, uint32_t offset, uint32_t size) { - if (bp->bios && offset + size < bp->bios_size) - return bp->bios + offset; - else + if (!bp->bios) return NULL; + + if (offset > bp->bios_size || size > bp->bios_size - offset) + return NULL; + + return bp->bios + offset; } #include "reg_helper.h" From 7ca695b3122297b06a3ed605bbe1cd32c85d9f5a Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Mon, 4 May 2026 16:14:11 -0400 Subject: [PATCH 135/697] drm/amd/display: Validate GPIO pin LUT table size before iterating commit 86d2b20644b11d21fe52c596e6e922b4590a3e3f upstream. [Why&How] The GPIO pin table parsers in get_gpio_i2c_info() and bios_parser_get_gpio_pin_info() derive an element count from the VBIOS table_header.structuresize field, then iterate over gpio_pin[] entries. However, GET_IMAGE() only validates that the table header itself fits within the BIOS image. If the VBIOS reports a structuresize larger than the actual mapped data, the loop reads past the end of the BIOS image, causing an out-of-bounds read. Fix this by calling bios_get_image() to validate that the full claimed structuresize is accessible within the BIOS image before entering the loop in both functions. Assisted-by: GitHub Copilot:claude-opus-4-6 Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ivan Lipski Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit ba5e95b43b773ae1bf1f66ee6b31eb774e65afe3) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index 550a9f1d03f8..e004458f0e43 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -492,6 +492,10 @@ static enum bp_result get_gpio_i2c_info( - sizeof(struct atom_common_table_header)) / sizeof(struct atom_gpio_pin_assignment); + if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut), + le16_to_cpu(header->table_header.structuresize))) + return BP_RESULT_BADBIOSTABLE; + pin = (struct atom_gpio_pin_assignment *) header->gpio_pin; for (table_index = 0; table_index < count; table_index++) { @@ -680,6 +684,11 @@ static enum bp_result bios_parser_get_gpio_pin_info( count = (le16_to_cpu(header->table_header.structuresize) - sizeof(struct atom_common_table_header)) / sizeof(struct atom_gpio_pin_assignment); + + if (!bios_get_image(&bp->base, DATA_TABLES(gpio_pin_lut), + le16_to_cpu(header->table_header.structuresize))) + return BP_RESULT_BADBIOSTABLE; + for (i = 0; i < count; ++i) { if (header->gpio_pin[i].gpio_id != gpio_id) continue; From 1ecde19bfce6535bffddad1139ff466b6d401b8e Mon Sep 17 00:00:00 2001 From: Harry Wentland Date: Thu, 7 May 2026 16:26:31 -0400 Subject: [PATCH 136/697] drm/amd/display: Validate payload length and link_index in dc_process_dmub_aux_transfer_async commit 6c92f6d9600efa3ef0d9e560a2b52776d9803c29 upstream. [Why&How] dc_process_dmub_aux_transfer_async() copies payload->length bytes into a 16-byte stack buffer (dpaux.data[16]) guarded only by an ASSERT(), which is a no-op in release builds. If a caller ever passes length > 16 this results in a stack buffer overflow via memcpy. Additionally, link_index is used to dereference dc->links[] without bounds checking against dc->link_count, risking an out-of-bounds access. Replace the ASSERT with a hard runtime check that returns false when payload->length exceeds the destination buffer size, and add a bounds check for link_index before it is used. Assisted-by: GitHub Copilot:Claude claude-4-opus Reviewed-by: Alex Hung Signed-off-by: Harry Wentland Signed-off-by: Ivan Lipski Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit ba4caa9fecdf7a38f98c878ad05a8a64148b6881) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/display/dc/core/dc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index 5f2d5638c819..0347174173f3 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -5884,7 +5884,11 @@ bool dc_process_dmub_aux_transfer_async(struct dc *dc, uint8_t action; union dmub_rb_cmd cmd = {0}; - ASSERT(payload->length <= 16); + if (link_index >= dc->link_count || !dc->links[link_index]) + return false; + + if (payload->length > sizeof(cmd.dp_aux_access.aux_control.dpaux.data)) + return false; cmd.dp_aux_access.header.type = DMUB_CMD__DP_AUX_ACCESS; cmd.dp_aux_access.header.payload_bytes = 0; From 1be1e99cbd5b74a69d3f92200ca87cf1bce852db Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 9 May 2026 22:44:12 +0200 Subject: [PATCH 137/697] batman-adv: v: stop OGMv2 on disabled interface commit f8ce8b8331a1bc44ad4905886a482214d428b253 upstream. When a batadv_hard_iface is disabled, its mesh_iface pointer is set to NULL. However, batadv_v_ogm_send_meshif() may still dispatch OGMs via batadv_v_ogm_queue_on_if() for interfaces that have since lost their mesh_iface association. This results in a NULL pointer dereference when batadv_v_ogm_queue_on_if() unconditionally calls netdev_priv() on the now NULL hard_iface->mesh_iface to retrieve the batadv_priv. It is necessary to ensure that the batadv_v_ogm_queue_on_if() checks that it is using the same mesh_iface for which batadv_v_ogm_send_meshif() was called. Cc: stable@kernel.org Fixes: 0da0035942d4 ("batman-adv: OGMv2 - add basic infrastructure") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reviewed-by: Yuan Tan Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bat_v_ogm.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index e3870492dab7..e955b4940c72 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -113,14 +113,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) /** * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface + * @bat_priv: the bat priv with all the mesh interface information * @skb: the OGM to send * @hard_iface: the interface to use to send the OGM */ -static void batadv_v_ogm_send_to_if(struct sk_buff *skb, +static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv, + struct sk_buff *skb, struct batadv_hard_iface *hard_iface) { - struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); - if (hard_iface->if_status != BATADV_IF_ACTIVE) { kfree_skb(skb); return; @@ -187,6 +187,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) /** * batadv_v_ogm_aggr_send() - flush & send aggregation queue + * @bat_priv: the bat priv with all the mesh interface information * @hard_iface: the interface with the aggregation queue to flush * * Aggregates all OGMv2 packets currently in the aggregation queue into a @@ -196,7 +197,8 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface) * * Caller needs to hold the hard_iface->bat_v.aggr_list.lock. */ -static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) +static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv, + struct batadv_hard_iface *hard_iface) { unsigned int aggr_len = hard_iface->bat_v.aggr_len; struct sk_buff *skb_aggr; @@ -226,27 +228,32 @@ static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface) consume_skb(skb); } - batadv_v_ogm_send_to_if(skb_aggr, hard_iface); + batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface); } /** * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface + * @bat_priv: the bat priv with all the mesh interface information * @skb: the OGM to queue * @hard_iface: the interface to queue the OGM on */ -static void batadv_v_ogm_queue_on_if(struct sk_buff *skb, +static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv, + struct sk_buff *skb, struct batadv_hard_iface *hard_iface) { - struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface); + if (hard_iface->mesh_iface != bat_priv->mesh_iface) { + kfree_skb(skb); + return; + } if (!atomic_read(&bat_priv->aggregated_ogms)) { - batadv_v_ogm_send_to_if(skb, hard_iface); + batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface); return; } spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); if (!batadv_v_ogm_queue_left(skb, hard_iface)) - batadv_v_ogm_aggr_send(hard_iface); + batadv_v_ogm_aggr_send(bat_priv, hard_iface); hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb); __skb_queue_tail(&hard_iface->bat_v.aggr_list, skb); @@ -343,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv) break; } - batadv_v_ogm_queue_on_if(skb_tmp, hard_iface); + batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface); batadv_hardif_put(hard_iface); } rcu_read_unlock(); @@ -383,12 +390,14 @@ void batadv_v_ogm_aggr_work(struct work_struct *work) { struct batadv_hard_iface_bat_v *batv; struct batadv_hard_iface *hard_iface; + struct batadv_priv *bat_priv; batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work); hard_iface = container_of(batv, struct batadv_hard_iface, bat_v); + bat_priv = netdev_priv(hard_iface->mesh_iface); spin_lock_bh(&hard_iface->bat_v.aggr_list.lock); - batadv_v_ogm_aggr_send(hard_iface); + batadv_v_ogm_aggr_send(bat_priv, hard_iface); spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock); batadv_v_ogm_start_queue_timer(hard_iface); @@ -578,7 +587,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, if_outgoing->net_dev->name, ntohl(ogm_forward->throughput), ogm_forward->ttl, if_incoming->net_dev->name); - batadv_v_ogm_queue_on_if(skb, if_outgoing); + batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing); out: batadv_orig_ifinfo_put(orig_ifinfo); From 23d4ce84df4d614f39ec3244f2a5a731d185d08d Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 14 May 2026 16:33:12 +0200 Subject: [PATCH 138/697] batman-adv: tvlv: abort OGM send on tvlv append failure commit 501368506563e151b322c8c3f228b796e615b90d upstream. batadv_tvlv_container_ogm_append() could fail in two ways: a memory allocation failure when resizing the packet buffer, or the tvlv data exceeding U16_MAX bytes. In both cases the function previously returned the old (now stale) tvlv_value_len rather than signalling an error, causing the OGM/OGM2 send path to transmit a packet whose TVLV length field no longer matched the actual buffer contents. And because it also didn't fill in the new TVLV data, sending either uninitialized or corrupted data on the wire. All errors in batadv_tvlv_container_ogm_append() must be forwarded to the caller. And the caller must abort the send of the OGM2. For B.A.T.M.A.N. IV, it is currently not allowed to abort the send. The non-TVLV part of the OGM must be queued up instead. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bat_iv_ogm.c | 16 +++++++++++++--- net/batman-adv/bat_v_ogm.c | 26 ++++++++++++++------------ net/batman-adv/tvlv.c | 17 ++++++++++++----- net/batman-adv/tvlv.h | 2 +- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 74ef7dc2b2f9..7ad26128b5f7 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -790,6 +790,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) u32 seqno; u16 tvlv_len = 0; unsigned long send_time; + int ret; lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); @@ -813,9 +814,18 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) * appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes(bat_priv); - tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, - ogm_buff_len, - BATADV_OGM_HLEN); + ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, + ogm_buff_len, + BATADV_OGM_HLEN); + if (ret < 0) { + /* OGMs must be queued even when the buffer allocation for + * TVLVs failed. just fall back to the non-TVLV version + */ + ret = 0; + *ogm_buff_len = BATADV_OGM_HLEN; + } + + tvlv_len = ret; } batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index e955b4940c72..d66ca77b1aaa 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -269,10 +269,10 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv) struct batadv_hard_iface *hard_iface; struct batadv_ogm2_packet *ogm_packet; struct sk_buff *skb, *skb_tmp; - unsigned char *ogm_buff; + unsigned char **ogm_buff; struct list_head *iter; - int ogm_buff_len; - u16 tvlv_len = 0; + int *ogm_buff_len; + u16 tvlv_len; int ret; lockdep_assert_held(&bat_priv->bat_v.ogm_buff_mutex); @@ -280,25 +280,27 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv) if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) goto out; - ogm_buff = bat_priv->bat_v.ogm_buff; - ogm_buff_len = bat_priv->bat_v.ogm_buff_len; + ogm_buff = &bat_priv->bat_v.ogm_buff; + ogm_buff_len = &bat_priv->bat_v.ogm_buff_len; + /* tt changes have to be committed before the tvlv data is * appended as it may alter the tt tvlv container */ batadv_tt_local_commit_changes(bat_priv); - tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff, - &ogm_buff_len, - BATADV_OGM2_HLEN); + ret = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, + ogm_buff_len, + BATADV_OGM2_HLEN); + if (ret < 0) + goto reschedule; - bat_priv->bat_v.ogm_buff = ogm_buff; - bat_priv->bat_v.ogm_buff_len = ogm_buff_len; + tvlv_len = ret; - skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len); + skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + *ogm_buff_len); if (!skb) goto reschedule; skb_reserve(skb, ETH_HLEN); - skb_put_data(skb, ogm_buff, ogm_buff_len); + skb_put_data(skb, *ogm_buff, *ogm_buff_len); ogm_packet = (struct batadv_ogm2_packet *)skb->data; ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 76dff1f9c559..ce158d6b6856 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -306,9 +307,10 @@ static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, * The ogm packet might be enlarged or shrunk depending on the current size * and the size of the to-be-appended tvlv containers. * - * Return: size of all appended tvlv containers in bytes. + * Return: size of all appended tvlv containers in bytes (max U16_MAX), negative + * if operation failed */ -u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, +int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len) { @@ -316,6 +318,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_len; void *tvlv_value; + int tvlv_len_ret; bool ret; spin_lock_bh(&bat_priv->tvlv.container_list_lock); @@ -323,9 +326,12 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, packet_min_len, tvlv_value_len); - - if (!ret) + if (!ret) { + tvlv_len_ret = -ENOMEM; goto end; + } + + tvlv_len_ret = tvlv_value_len; if (!tvlv_value_len) goto end; @@ -344,7 +350,8 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, end: spin_unlock_bh(&bat_priv->tvlv.container_list_lock); - return tvlv_value_len; + + return tvlv_len_ret; } /** diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index e5697230d991..f96f6b3f44a0 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -16,7 +16,7 @@ void batadv_tvlv_container_register(struct batadv_priv *bat_priv, u8 type, u8 version, void *tvlv_value, u16 tvlv_value_len); -u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, +int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, From ede47988ac5687793745b17c1634a496a2299919 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 9 May 2026 21:55:29 +0200 Subject: [PATCH 139/697] batman-adv: tvlv: reject oversized TVLV packets commit f50487e3566358b2b982b7801945e858c78ad9ab upstream. batadv_tvlv_container_ogm_append() builds a TVLV packet section from the tvlv.container_list. The total size of this section is computed by batadv_tvlv_container_list_size(), which sums the sizes of all registered containers. The return type and accumulator in batadv_tvlv_container_list_size() were u16. If the accumulated size exceeds U16_MAX, the value wraps around, causing the subsequent allocation in batadv_tvlv_container_ogm_append() to be undersized. The memcpy-style copy that follows would then write beyond the end of the allocated buffer, corrupting kernel memory. Fix this by widening the return type of batadv_tvlv_container_list_size() to size_t. In batadv_tvlv_container_ogm_append(), check the computed length against U16_MAX before proceeding, and bail out as if the allocation had failed when the limit is exceeded. Cc: stable@kernel.org Fixes: ef26157747d4 ("batman-adv: tvlv - basic infrastructure") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reviewed-by: Yuan Tan Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tvlv.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index ce158d6b6856..cde798c82dcf 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -160,10 +161,10 @@ batadv_tvlv_container_get(struct batadv_priv *bat_priv, u8 type, u8 version) * * Return: size of all currently registered tvlv containers in bytes. */ -static u16 batadv_tvlv_container_list_size(struct batadv_priv *bat_priv) +static size_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv) { struct batadv_tvlv_container *tvlv; - u16 tvlv_len = 0; + size_t tvlv_len = 0; lockdep_assert_held(&bat_priv->tvlv.container_list_lock); @@ -316,13 +317,17 @@ int batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, { struct batadv_tvlv_container *tvlv; struct batadv_tvlv_hdr *tvlv_hdr; - u16 tvlv_value_len; + size_t tvlv_value_len; void *tvlv_value; int tvlv_len_ret; bool ret; spin_lock_bh(&bat_priv->tvlv.container_list_lock); tvlv_value_len = batadv_tvlv_container_list_size(bat_priv); + if (tvlv_value_len > U16_MAX) { + tvlv_len_ret = -E2BIG; + goto end; + } ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, packet_min_len, tvlv_value_len); From ca3ff3d2a0af42a1636f4c01087bc50923ce50d4 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 15 May 2026 22:00:40 +0200 Subject: [PATCH 140/697] batman-adv: iv: recover OGM scheduling after forward packet error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit aa3153bd139a6c48667dcd02608d3b2c80bff02c upstream. When batadv_iv_ogm_schedule_buff() fails to allocate and queue a forward packet for OGM transmission, the work item that drives periodic OGM scheduling is never re-armed. This silently halts transmission of the node's own OGMs on the affected interface — only OGMs from other peers continue to be aggregated and forwarded. Fix this by tracking whether batadv_iv_ogm_queue_add() (and transitively batadv_iv_ogm_aggregate_new()) successfully scheduled a forward packet. When scheduling fails, batadv_iv_ogm_schedule_buff() falls back to queuing a dedicated recovery work item (reschedule_work) that fires after one originator interval and calls batadv_iv_ogm_schedule() again. Cc: stable@kernel.org Fixes: c6c8fea29769 ("net: Add batman-adv meshing protocol") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bat_iv_ogm.c | 76 +++++++++++++++++++++++++++---------- net/batman-adv/types.h | 3 ++ 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 7ad26128b5f7..b8b1b997960a 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -224,6 +224,8 @@ static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface) hard_iface->bat_iv.ogm_buff = NULL; mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); + + cancel_delayed_work_sync(&hard_iface->bat_iv.reschedule_work); } static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface) @@ -536,8 +538,10 @@ batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet, * @if_incoming: interface where the packet was received * @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm + * + * Return: whether forward packet was scheduled */ -static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, +static bool batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, int packet_len, unsigned long send_time, bool direct_link, struct batadv_hard_iface *if_incoming, @@ -561,13 +565,13 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, skb = netdev_alloc_skb_ip_align(NULL, skb_size); if (!skb) - return; + return false; forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, queue_left, bat_priv, skb); if (!forw_packet_aggr) { kfree_skb(skb); - return; + return false; } forw_packet_aggr->skb->priority = TC_PRIO_CONTROL; @@ -590,6 +594,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, batadv_iv_send_outstanding_bat_ogm_packet); batadv_forw_packet_ogmv1_queue(bat_priv, forw_packet_aggr, send_time); + + return true; } /* aggregate a new packet into the existing ogm packet */ @@ -617,8 +623,10 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, * @if_outgoing: interface for which the retransmission should be considered * @own_packet: true if it is a self-generated ogm * @send_time: timestamp (jiffies) when the packet is to be sent + * + * Return: whether forward packet was scheduled */ -static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, +static bool batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, unsigned char *packet_buff, int packet_len, struct batadv_hard_iface *if_incoming, @@ -670,14 +678,16 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, if (!own_packet && atomic_read(&bat_priv->aggregated_ogms)) send_time += max_aggregation_jiffies; - batadv_iv_ogm_aggregate_new(packet_buff, packet_len, - send_time, direct_link, - if_incoming, if_outgoing, - own_packet); + return batadv_iv_ogm_aggregate_new(packet_buff, packet_len, + send_time, direct_link, + if_incoming, if_outgoing, + own_packet); } else { batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff, packet_len, direct_link); spin_unlock_bh(&bat_priv->forw_bat_list_lock); + + return true; } } @@ -790,6 +800,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) u32 seqno; u16 tvlv_len = 0; unsigned long send_time; + bool reschedule = false; + bool scheduled; int ret; lockdep_assert_held(&hard_iface->bat_iv.ogm_buff_mutex); @@ -818,11 +830,8 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) ogm_buff_len, BATADV_OGM_HLEN); if (ret < 0) { - /* OGMs must be queued even when the buffer allocation for - * TVLVs failed. just fall back to the non-TVLV version - */ - ret = 0; - *ogm_buff_len = BATADV_OGM_HLEN; + reschedule = true; + goto out; } tvlv_len = ret; @@ -844,8 +853,11 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) /* OGMs from secondary interfaces are only scheduled on their * respective interfaces. */ - batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, - hard_iface, hard_iface, 1, send_time); + scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len, + hard_iface, hard_iface, 1, send_time); + if (!scheduled) + reschedule = true; + goto out; } @@ -857,15 +869,28 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface) if (!kref_get_unless_zero(&tmp_hard_iface->refcount)) continue; - batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, - *ogm_buff_len, hard_iface, - tmp_hard_iface, 1, send_time); - + scheduled = batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, + *ogm_buff_len, hard_iface, + tmp_hard_iface, 1, send_time); batadv_hardif_put(tmp_hard_iface); + + if (!scheduled && tmp_hard_iface == hard_iface) + reschedule = true; } rcu_read_unlock(); out: + if (reschedule) { + /* there was a failure scheduling the own forward packet. + * as result, the batadv_iv_send_outstanding_bat_ogm_packet() + * work item is no longer scheduled. it is therefore necessary + * to reschedule it manually + */ + queue_delayed_work(batadv_event_workqueue, + &hard_iface->bat_iv.reschedule_work, + msecs_to_jiffies(atomic_read(&bat_priv->orig_interval))); + } + batadv_hardif_put(primary_if); } @@ -880,6 +905,17 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) mutex_unlock(&hard_iface->bat_iv.ogm_buff_mutex); } +static void batadv_iv_ogm_reschedule(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct batadv_hard_iface *hard_iface; + + hard_iface = container_of(delayed_work, + struct batadv_hard_iface, + bat_iv.reschedule_work); + batadv_iv_ogm_schedule(hard_iface); +} + /** * batadv_iv_orig_ifinfo_sum() - Get bcast_own sum for originator over interface * @orig_node: originator which reproadcasted the OGMs directly @@ -2272,6 +2308,8 @@ batadv_iv_ogm_neigh_is_sob(struct batadv_neigh_node *neigh1, static void batadv_iv_iface_enabled(struct batadv_hard_iface *hard_iface) { + INIT_DELAYED_WORK(&hard_iface->bat_iv.reschedule_work, batadv_iv_ogm_reschedule); + /* begin scheduling originator messages on that interface */ batadv_iv_ogm_schedule(hard_iface); } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 41c1d19f786b..83ca23a317b3 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -83,6 +83,9 @@ struct batadv_hard_iface_bat_iv { /** @ogm_seqno: OGM sequence number - used to identify each OGM */ atomic_t ogm_seqno; + /** @reschedule_work: recover OGM schedule after schedule error */ + struct delayed_work reschedule_work; + /** @ogm_buff_mutex: lock protecting ogm_buff and ogm_buff_len */ struct mutex ogm_buff_mutex; }; From 8a3707653ab658e082ccd992e92594e01b09a3fc Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 14 May 2026 19:22:02 +0200 Subject: [PATCH 141/697] batman-adv: mcast: fix use-after-free in orig_node RCU release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 20c2d6a20ca936f5aaa6dd40f73f262ac45c87cc upstream. batadv_mcast_purge_orig() removes entries from RCU-protected hlists but does not wait for an RCU grace period before returning. Concurrent RCU readers may still accesses references to those entries at the point of removal. RCU-protected readers trying to operate on entries like orig->mcast_want_all_ipv6_node will then access already freed memory. Fix this by moving batadv_mcast_purge_orig() to batadv_orig_node_release(), just before the call_rcu() invocation. This ensures RCU readers that were active at purge time have drained before the orig_node memory is reclaimed. Cc: stable@kernel.org Fixes: ab49886e3da7 ("batman-adv: Add IPv4 link-local/IPv6-ll-all-nodes multicast support") Acked-by: Linus Lüssing Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/originator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index a662408ad867..ae195053e060 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -835,8 +835,6 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) orig_node = container_of(rcu, struct batadv_orig_node, rcu); - batadv_mcast_purge_orig(orig_node); - batadv_frag_purge_orig(orig_node, NULL); kfree(orig_node->tt_buff); @@ -887,6 +885,8 @@ void batadv_orig_node_release(struct kref *ref) } spin_unlock_bh(&orig_node->vlan_list_lock); + batadv_mcast_purge_orig(orig_node); + call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); } From ae7aeb0ce3c0ebbe357ed525779acac197a18086 Mon Sep 17 00:00:00 2001 From: Ruijie Li Date: Thu, 14 May 2026 16:13:25 +0800 Subject: [PATCH 142/697] batman-adv: clear current gateway during teardown commit a340a51ed801eab7bb454150c226323b865263cc upstream. batadv_gw_node_free() removes the gateway list entries during mesh teardown, but it does not clear the currently selected gateway. This leaves stale gateway state behind across cleanup and can break a later mesh recreation. Clear bat_priv->gw.curr_gw before walking the gateway list so the selected gateway reference is dropped as part of teardown. Fixes: 2265c1410864 ("batman-adv: gateway election code refactoring") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Ruijie Li Signed-off-by: Zhanpeng Li Signed-off-by: Ren Wei Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/gateway_client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 7a11b245e9f4..ff341c270d91 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -478,10 +478,14 @@ void batadv_gw_node_delete(struct batadv_priv *bat_priv, */ void batadv_gw_node_free(struct batadv_priv *bat_priv) { + struct batadv_gw_node *curr_gw; struct batadv_gw_node *gw_node; struct hlist_node *node_tmp; spin_lock_bh(&bat_priv->gw.list_lock); + curr_gw = rcu_replace_pointer(bat_priv->gw.curr_gw, NULL, true); + batadv_gw_node_put(curr_gw); + hlist_for_each_entry_safe(gw_node, node_tmp, &bat_priv->gw.gateway_list, list) { hlist_del_init_rcu(&gw_node->list); From 9cceea8eeba710def2a5707ee00f00c74a9a1cac Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 13 May 2026 09:01:34 +0200 Subject: [PATCH 143/697] batman-adv: dat: handle forward allocation error commit 2d8826a2d3657cea66fb0370f9e521575a673871 upstream. batadv_dat_forward_data() calls pskb_copy_for_clone() to duplicate an skb for each DHT candidate, but does not check the return value before passing it to batadv_send_skb_prepare_unicast_4addr(). That function dereferences the skb unconditionally, so a failed allocation triggers a NULL pointer dereference. Skip forwarding to the current DHT candidate on allocation failure. Cc: stable@kernel.org Fixes: 785ea1144182 ("batman-adv: Distributed ARP Table - create DHT helper functions") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reviewed-by: Yuan Tan Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/distributed-arp-table.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 8b8132eb0a79..031c295fff1b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -697,6 +697,9 @@ static bool batadv_dat_forward_data(struct batadv_priv *bat_priv, goto free_orig; tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC); + if (!tmp_skb) + goto free_neigh; + if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb, cand[i].orig_node, packet_subtype)) { From 3eb8bcb823391bd58997831b3c9c152a4ba8e255 Mon Sep 17 00:00:00 2001 From: Ruide Cao Date: Wed, 13 May 2026 11:58:15 +0800 Subject: [PATCH 144/697] batman-adv: fix fragment reassembly length accounting commit 9cd3f16c320bfdadd4509358122368deb56a5741 upstream. batman-adv keeps a running payload length for queued fragments and uses it to validate a fragment chain before reassembly. That accounting currently allows the accumulated fragment length to be truncated during updates. As a result, malformed fragment chains can bypass the intended validation and drive reassembly with inconsistent length state, leading to a local denial of service. Fix the accounting by storing the accumulated length in a length-typed field and rejecting update overflows before the existing validation logic runs. The fix was verified against the original reproducer and against valid fragment reassembly paths. Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Ruide Cao Tested-by: Ren Wei Signed-off-by: Ren Wei Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 23 +++++++++++++++++------ net/batman-adv/types.h | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index cc14bc41381e..d462d5501d55 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -80,9 +81,9 @@ void batadv_frag_purge_orig(struct batadv_orig_node *orig_node, * * Return: the maximum size of payload that can be fragmented. */ -static int batadv_frag_size_limit(void) +static size_t batadv_frag_size_limit(void) { - int limit = BATADV_FRAG_MAX_FRAG_SIZE; + size_t limit = BATADV_FRAG_MAX_FRAG_SIZE; limit -= sizeof(struct batadv_frag_packet); limit *= BATADV_FRAG_MAX_FRAGMENTS; @@ -143,7 +144,9 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, struct batadv_frag_packet *frag_packet; u8 bucket; u16 seqno, hdr_size = sizeof(struct batadv_frag_packet); + bool overflow = false; bool ret = false; + size_t data_len; /* Linearize packet to avoid linearizing 16 packets in a row when doing * the later merge. Non-linear merge should be added to remove this @@ -153,6 +156,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, goto err; frag_packet = (struct batadv_frag_packet *)skb->data; + data_len = skb->len - hdr_size; seqno = ntohs(frag_packet->seqno); bucket = seqno % BATADV_FRAG_BUFFER_COUNT; @@ -171,7 +175,7 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, spin_lock_bh(&chain->lock); if (batadv_frag_init_chain(chain, seqno)) { hlist_add_head(&frag_entry_new->list, &chain->fragment_list); - chain->size = skb->len - hdr_size; + chain->size = data_len; chain->timestamp = jiffies; chain->total_size = ntohs(frag_packet->total_size); ret = true; @@ -188,7 +192,11 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, if (frag_entry_curr->no < frag_entry_new->no) { hlist_add_before(&frag_entry_new->list, &frag_entry_curr->list); - chain->size += skb->len - hdr_size; + + if (check_add_overflow(chain->size, data_len, + &chain->size)) + overflow = true; + chain->timestamp = jiffies; ret = true; goto out; @@ -201,13 +209,16 @@ static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, /* Reached the end of the list, so insert after 'frag_entry_last'. */ if (likely(frag_entry_last)) { hlist_add_behind(&frag_entry_new->list, &frag_entry_last->list); - chain->size += skb->len - hdr_size; + + if (check_add_overflow(chain->size, data_len, &chain->size)) + overflow = true; + chain->timestamp = jiffies; ret = true; } out: - if (chain->size > batadv_frag_size_limit() || + if (overflow || chain->size > batadv_frag_size_limit() || chain->total_size != ntohs(frag_packet->total_size) || chain->total_size > batadv_frag_size_limit()) { /* Clear chain if total size of either the list or the packet diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 83ca23a317b3..aca82693ccff 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -304,7 +304,7 @@ struct batadv_frag_table_entry { u16 seqno; /** @size: accumulated size of packets in list */ - u16 size; + size_t size; /** @total_size: expected size of the assembled packet */ u16 total_size; From 90ae3eae06b7b8ab9f6250b9497c860915b4c17b Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Mon, 11 May 2026 18:52:09 +0200 Subject: [PATCH 145/697] batman-adv: fix tp_meter counter underflow during shutdown commit 94f3b133168d1c49895e7cc6afbcf1cc0b354602 upstream. batadv_tp_sender_shutdown() unconditionally decrements the "sending" atomic counter. If multiple paths (e.g. timeout, user cancel, and normal finish) call this function, the counter can underflow to -1. Since the sender logic treats any non-zero value as "still sending", a negative value causes the sender kthread to loop indefinitely. This leads to a use-after-free when the interface is removed while the zombie thread is still active. Fix this by using atomic_xchg() to ensure the counter only transitions from 1 to 0 once. Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei [sven: added missing change in batadv_tp_send] Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 76ece4014384..e279d42fe082 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -451,7 +451,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars, enum batadv_tp_meter_reason reason) { - if (!atomic_dec_and_test(&tp_vars->sending)) + if (atomic_xchg(&tp_vars->sending, 0) != 1) return; tp_vars->reason = reason; @@ -885,7 +885,7 @@ static int batadv_tp_send(void *arg) "Meter: %s() cannot send packets (%d)\n", __func__, err); /* ensure nobody else tries to stop the thread now */ - if (atomic_dec_and_test(&tp_vars->sending)) + if (atomic_xchg(&tp_vars->sending, 0) == 1) tp_vars->reason = err; break; } From 5895ad21c7059a652da83fb817510f7a1e962abf Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 13 May 2026 09:01:36 +0200 Subject: [PATCH 146/697] batman-adv: frag: disallow unicast fragment in fragment commit bc62216dc8e221e3781afa14430f45208bfa9af9 upstream. batadv_frag_skb_buffer() is called by batadv_batman_skb_recv() when a BATADV_UNICAST_FRAG packet is received. Once all fragments are collected and the packet is reassembled, batadv_recv_frag_packet() calls batadv_batman_skb_recv() again to process the defragmented payload. A malicious sender can craft a BATADV_UNICAST_FRAG packet whose reassembled payload is itself a BATADV_UNICAST_FRAG packet (matryoshka-style nesting). Each nesting level recurses through batadv_batman_skb_recv() without bound, growing the kernel stack until it is exhausted. Since refragmentation or fragments in fragments are not actually allowed, discard all packets which are still BATADV_UNICAST_FRAG packets after the defragmentation process. Cc: stable@kernel.org Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reviewed-by: Yuan Tan Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/fragmentation.c | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c index d462d5501d55..31395281692c 100644 --- a/net/batman-adv/fragmentation.c +++ b/net/batman-adv/fragmentation.c @@ -304,6 +304,31 @@ batadv_frag_merge_packets(struct hlist_head *chain) return skb_out; } +/** + * batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet + * @skb: newly merged skb + * + * Return: if newly skb is of type BATADV_UNICAST_FRAG + */ +static bool batadv_skb_is_frag(struct sk_buff *skb) +{ + struct batadv_ogm_packet *batadv_ogm_packet; + + /* packet should hold at least type and version */ + if (unlikely(!pskb_may_pull(skb, 2))) + return false; + + batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data; + + if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION) + return false; + + if (batadv_ogm_packet->packet_type != BATADV_UNICAST_FRAG) + return false; + + return true; +} + /** * batadv_frag_skb_buffer() - buffer fragment for later merge * @skb: skb to buffer @@ -337,6 +362,16 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb, if (!skb_out) goto out_err; + /* fragment in fragment is not allowed. otherwise it is possible + * to exhaust the stack when receiving a matryoshka-style + * "fragments in a fragment packet" + */ + if (batadv_skb_is_frag(skb_out)) { + kfree_skb(skb_out); + skb_out = NULL; + goto out_err; + } + out: ret = true; out_err: From c6de1a5a9c406e30b91f1515a6ce05cc84023baa Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 10 May 2026 11:43:20 +0200 Subject: [PATCH 147/697] batman-adv: bla: fix report_work leak on backbone_gw purge commit 0459430add32ea41f3e2ef9351610e6d33627a6b upstream. batadv_bla_purge_backbone_gw() removes stale backbone gateway entries, but fails to properly handle their associated report_work: - If report_work is running, the purge must wait for it to finish before freeing the backbone_gw, otherwise the worker may access freed memory (e.g. bat_priv). - If report_work is pending, the purge must cancel it and release the reference held for that pending work item. The previous implementation called hlist_for_each_entry_safe() inside a spin_lock_bh() section, but cancel_work_sync() may sleep and therefore cannot be called from within a spinlock-protected region. Restructure the loop to handle one entry per spinlock critical section: acquire the lock, find the next entry to purge, remove it from the hash list, then release the lock before calling cancel_work_sync() and dropping the hash_entry reference. Repeat until no more entries require purging. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Reviewed-by: Simon Wunderlich Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bridge_loop_avoidance.c | 54 +++++++++++++++++--------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 2d7971424aa0..33b1275fdf83 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -1225,6 +1225,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) struct hlist_head *head; struct batadv_hashtable *hash; spinlock_t *list_lock; /* protects write access to the hash lists */ + bool purged; int i; hash = bat_priv->bla.backbone_hash; @@ -1235,30 +1236,45 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) head = &hash->table[i]; list_lock = &hash->list_locks[i]; - spin_lock_bh(list_lock); - hlist_for_each_entry_safe(backbone_gw, node_tmp, - head, hash_entry) { - if (now) - goto purge_now; - if (!batadv_has_timed_out(backbone_gw->lasttime, - BATADV_BLA_BACKBONE_TIMEOUT)) - continue; + do { + purged = false; - batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, - "%s(): backbone gw %pM timed out\n", - __func__, backbone_gw->orig); + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(backbone_gw, node_tmp, + head, hash_entry) { + if (now) + goto purge_now; + if (!batadv_has_timed_out(backbone_gw->lasttime, + BATADV_BLA_BACKBONE_TIMEOUT)) + continue; + + batadv_dbg(BATADV_DBG_BLA, backbone_gw->bat_priv, + "%s(): backbone gw %pM timed out\n", + __func__, backbone_gw->orig); purge_now: - /* don't wait for the pending request anymore */ - if (atomic_read(&backbone_gw->request_sent)) - atomic_dec(&bat_priv->bla.num_requests); + purged = true; - batadv_bla_del_backbone_claims(backbone_gw); + /* don't wait for the pending request anymore */ + if (atomic_read(&backbone_gw->request_sent)) + atomic_dec(&bat_priv->bla.num_requests); - hlist_del_rcu(&backbone_gw->hash_entry); - batadv_backbone_gw_put(backbone_gw); - } - spin_unlock_bh(list_lock); + batadv_bla_del_backbone_claims(backbone_gw); + + hlist_del_rcu(&backbone_gw->hash_entry); + break; + } + spin_unlock_bh(list_lock); + + if (purged) { + /* reference for pending report_work */ + if (cancel_work_sync(&backbone_gw->report_work)) + batadv_backbone_gw_put(backbone_gw); + + /* reference for hash_entry */ + batadv_backbone_gw_put(backbone_gw); + } + } while (purged); } } From 45384612f29692fbf0c770200361a7acff90125c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 12 May 2026 09:13:31 +0200 Subject: [PATCH 148/697] batman-adv: bla: avoid double decrement of bla.num_requests commit 83ab69bd12b80f6ea169c8bea6977701b53a043d upstream. The bla.num_requests is increased when no request_sent was in progress. And it is decremented in various places (announcement was received, backbone is purged, periodic work). But the check if the request_sent is actually set to a specific state and the atomic_dec/_inc are not safe because they are not atomic (TOCTOU) and multiple such code portions can run concurrently. At the same time, it is necessary to modify request_sent (state) and bla.num_requests atomically. Otherwise batadv_bla_send_request() might set request_sent to 1 and is interrupted. batadv_handle_announce() can then set request_sent back to 0 and decrement num_requests before batadv_bla_send_request() incremented it. The two operations must therefore be locked. And since state (request_sent) and wait_periods are only accessed inside this lock, they can be converted to simpler datatypes. And to avoid that the bla.num_requests is touched by a parallel running context with a valid backbone_gw reference after batadv_bla_purge_backbone_gw() ran, a third state "stopped" is required to correctly signal that a backbone_gw is in the state of being cleaned up. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bridge_loop_avoidance.c | 51 ++++++++++++++++++-------- net/batman-adv/mesh-interface.c | 1 + net/batman-adv/types.h | 39 ++++++++++++++++---- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 33b1275fdf83..42ee7efc3d79 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -515,8 +515,8 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; spin_lock_init(&entry->crc_lock); - atomic_set(&entry->request_sent, 0); - atomic_set(&entry->wait_periods, 0); + entry->state = BATADV_BLA_BACKBONE_GW_SYNCED; + entry->wait_periods = 0; ether_addr_copy(entry->orig, orig); INIT_WORK(&entry->report_work, batadv_bla_loopdetect_report); kref_init(&entry->refcount); @@ -545,9 +545,13 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, batadv_bla_send_announce(bat_priv, entry); /* this will be decreased in the worker thread */ - atomic_inc(&entry->request_sent); - atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS); - atomic_inc(&bat_priv->bla.num_requests); + spin_lock_bh(&bat_priv->bla.num_requests_lock); + if (entry->state == BATADV_BLA_BACKBONE_GW_SYNCED) { + entry->state = BATADV_BLA_BACKBONE_GW_UNSYNCED; + entry->wait_periods = BATADV_BLA_WAIT_PERIODS; + atomic_inc(&bat_priv->bla.num_requests); + } + spin_unlock_bh(&bat_priv->bla.num_requests_lock); } return entry; @@ -650,10 +654,12 @@ static void batadv_bla_send_request(struct batadv_bla_backbone_gw *backbone_gw) backbone_gw->vid, BATADV_CLAIM_TYPE_REQUEST); /* no local broadcasts should be sent or received, for now. */ - if (!atomic_read(&backbone_gw->request_sent)) { + spin_lock_bh(&backbone_gw->bat_priv->bla.num_requests_lock); + if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_SYNCED) { + backbone_gw->state = BATADV_BLA_BACKBONE_GW_UNSYNCED; atomic_inc(&backbone_gw->bat_priv->bla.num_requests); - atomic_set(&backbone_gw->request_sent, 1); } + spin_unlock_bh(&backbone_gw->bat_priv->bla.num_requests_lock); } /** @@ -874,10 +880,12 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, /* if we have sent a request and the crc was OK, * we can allow traffic again. */ - if (atomic_read(&backbone_gw->request_sent)) { + spin_lock_bh(&bat_priv->bla.num_requests_lock); + if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) { + backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED; atomic_dec(&backbone_gw->bat_priv->bla.num_requests); - atomic_set(&backbone_gw->request_sent, 0); } + spin_unlock_bh(&bat_priv->bla.num_requests_lock); } batadv_backbone_gw_put(backbone_gw); @@ -1256,9 +1264,13 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) purged = true; /* don't wait for the pending request anymore */ - if (atomic_read(&backbone_gw->request_sent)) + spin_lock_bh(&bat_priv->bla.num_requests_lock); + if (backbone_gw->state == BATADV_BLA_BACKBONE_GW_UNSYNCED) atomic_dec(&bat_priv->bla.num_requests); + backbone_gw->state = BATADV_BLA_BACKBONE_GW_STOPPED; + spin_unlock_bh(&bat_priv->bla.num_requests_lock); + batadv_bla_del_backbone_claims(backbone_gw); hlist_del_rcu(&backbone_gw->hash_entry); @@ -1509,7 +1521,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) batadv_bla_send_loopdetect(bat_priv, backbone_gw); - /* request_sent is only set after creation to avoid + /* state is only set to unsynced after creation to avoid * problems when we are not yet known as backbone gw * in the backbone. * @@ -1518,14 +1530,21 @@ static void batadv_bla_periodic_work(struct work_struct *work) * some grace time. */ - if (atomic_read(&backbone_gw->request_sent) == 0) - continue; + spin_lock_bh(&bat_priv->bla.num_requests_lock); + if (backbone_gw->state != BATADV_BLA_BACKBONE_GW_UNSYNCED) + goto unlock_next; - if (!atomic_dec_and_test(&backbone_gw->wait_periods)) - continue; + if (backbone_gw->wait_periods > 0) + backbone_gw->wait_periods--; + + if (backbone_gw->wait_periods > 0) + goto unlock_next; + backbone_gw->state = BATADV_BLA_BACKBONE_GW_SYNCED; atomic_dec(&backbone_gw->bat_priv->bla.num_requests); - atomic_set(&backbone_gw->request_sent, 0); + +unlock_next: + spin_unlock_bh(&bat_priv->bla.num_requests_lock); } rcu_read_unlock(); } diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index df7e95811ef5..dcdc82f6af8c 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -787,6 +787,7 @@ static int batadv_meshif_init_late(struct net_device *dev) atomic_set(&bat_priv->tt.ogm_append_cnt, 0); #ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bla.num_requests, 0); + spin_lock_init(&bat_priv->bla.num_requests_lock); #endif atomic_set(&bat_priv->tp_num, 0); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index aca82693ccff..d9518d73c257 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1026,6 +1026,12 @@ struct batadv_priv_bla { /** @num_requests: number of bla requests in flight */ atomic_t num_requests; + /** + * @num_requests_lock: locks update num_requests + + * batadv_backbone_gw::state + batadv_backbone_gw::wait_periods update + */ + spinlock_t num_requests_lock; + /** * @claim_hash: hash table containing mesh nodes this host has claimed */ @@ -1669,6 +1675,27 @@ struct batadv_priv { #ifdef CONFIG_BATMAN_ADV_BLA +enum batadv_bla_backbone_gw_state { + /** + * @BATADV_BLA_BACKBONE_GW_STOPPED: backbone gw is being removed + * and it must not longer work on requests + */ + BATADV_BLA_BACKBONE_GW_STOPPED, + + /** + * @BATADV_BLA_BACKBONE_GW_UNSYNCED: backbone was detected out + * of sync and a request was send. No traffic is forwarded until the + * situation is resolved + */ + BATADV_BLA_BACKBONE_GW_UNSYNCED, + + /** + * @BATADV_BLA_BACKBONE_GW_SYNCED: backbone is consider to be in + * sync. traffic can be forwarded + */ + BATADV_BLA_BACKBONE_GW_SYNCED, +}; + /** * struct batadv_bla_backbone_gw - batman-adv gateway bridged into the LAN */ @@ -1694,16 +1721,12 @@ struct batadv_bla_backbone_gw { /** * @wait_periods: grace time for bridge forward delays and bla group * forming at bootup phase - no bcast traffic is formwared until it has - * elapsed + * elapsed. Must only be access with num_requests_lock. */ - atomic_t wait_periods; + u8 wait_periods; - /** - * @request_sent: if this bool is set to true we are out of sync with - * this backbone gateway - no bcast traffic is formwared until the - * situation was resolved - */ - atomic_t request_sent; + /** @state: sync state. Must only be access with num_requests_lock. */ + enum batadv_bla_backbone_gw_state state; /** @crc: crc16 checksum over all claims */ u16 crc; From 6921a7683ae9ad0208d829e71f725a9e25ccff49 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 19 May 2026 09:23:49 +0200 Subject: [PATCH 149/697] batman-adv: bla: avoid NULL-ptr deref for claim via dropped interface commit f80d3d98d2ff78d9e2fe5d68b1f45948c4f7bd24 upstream. Without rtnl_lock held, a hardif might be retrieved as primary interface of a meshif, but then (while operating on this interface) getting decoupled from the mesh interface. In this case, the meshif still exists but the pointer from the primary hardif to the meshif is set to NULL. The mesh_iface must be checked first to be non-NULL before continuing to send an ARP request using meshif. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Reported-by: Ido Schimmel Reported-by: syzbot+9fdcc9f05a98a540b816@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9fdcc9f05a98a540b816 Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/bridge_loop_avoidance.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 42ee7efc3d79..3072f94275ac 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -357,12 +357,14 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac, sizeof(local_claim_dest)); local_claim_dest.type = claimtype; - mesh_iface = primary_if->mesh_iface; + mesh_iface = READ_ONCE(primary_if->mesh_iface); + if (!mesh_iface) + goto out; skb = arp_create(ARPOP_REPLY, ETH_P_ARP, /* IP DST: 0.0.0.0 */ zeroip, - primary_if->mesh_iface, + mesh_iface, /* IP SRC: 0.0.0.0 */ zeroip, /* Ethernet DST: Broadcast */ From dc2ae5fbd2dadc26735092f140b246841d969a11 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 13 May 2026 09:01:35 +0200 Subject: [PATCH 150/697] batman-adv: tp_meter: avoid use of uninit sender vars commit 6c65cf23d4c6170fcf5714c32aa64689718cb142 upstream. batadv_tp_recv_ack() and batadv_tp_stop() are only valid for tp_vars in the BATADV_TP_SENDER role. When called with a BATADV_TP_RECEIVER role, it proceeds to read sender-only members that were never initialized, leading to undefined behavior. This can be triggered when a node that is currently acting as a receiver in an ongoing tp_meter session receives a malicious ACK packet. Guard against this by checking tp_vars->role immediately after the lookup and bailing out if it is not BATADV_TP_SENDER, before any of those members are accessed. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reviewed-by: Yuan Tan Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e279d42fe082..341d6d5083e1 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -663,6 +663,9 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (unlikely(!tp_vars)) return; + if (unlikely(tp_vars->role != BATADV_TP_SENDER)) + goto out; + if (unlikely(atomic_read(&tp_vars->sending) == 0)) goto out; @@ -1100,12 +1103,16 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: trying to interrupt an already over connection\n"); - goto out; + goto out_put_orig_node; } + if (unlikely(tp_vars->role != BATADV_TP_SENDER)) + goto out_put_tp_vars; + batadv_tp_sender_shutdown(tp_vars, return_value); +out_put_tp_vars: batadv_tp_vars_put(tp_vars); -out: +out_put_orig_node: batadv_orig_node_put(orig_node); } From 770bf0a35f0620b526fd4193889d1e77084e4c43 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 13 May 2026 10:43:54 +0200 Subject: [PATCH 151/697] batman-adv: tp_meter: directly shut down timer on cleanup commit d5487249a81ea658717614009c8f46acc5b7101a upstream. batadv_tp_sender_cleanup() was calling timer_delete_sync() followed by timer_delete() to guard against the timer handler re-arming itself between the two calls. This double-deletion hack relied on the sending status being set to 0 to suppress re-arming. Replace both calls with a single timer_shutdown_sync(). This function both waits for any running timer callback to complete (like timer_delete_sync()) and permanently disarms the timer so it cannot be re-armed afterwards, making re-arming prevention unconditional and self-documenting. The re-arming property is also required because otherwise: 1. context 0 (batadv_tp_recv_ack()) checks in batadv_tp_reset_sender_timer() if sending is still 1 -> it is 2. context 1 changes in batadv_tp_sender_shutdown() sending to 0 and in this process forces the kthread to stop timer in batadv_tp_sender_cleanup() 3. context 0 continues in batadv_tp_reset_sender_timer() and rearms the timer -> but the reference for it is already gone Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 341d6d5083e1..e0d7f98e5b9d 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -400,13 +400,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) batadv_tp_list_detach(tp_vars); /* kill the timer and remove its reference */ - timer_delete_sync(&tp_vars->timer); - /* the worker might have rearmed itself therefore we kill it again. Note - * that if the worker should run again before invoking the following - * timer_delete(), it would not re-arm itself once again because the status - * is OFF now - */ - timer_delete(&tp_vars->timer); + timer_shutdown_sync(&tp_vars->timer); batadv_tp_vars_put(tp_vars); } From b285bc0a97f43823a4967fb6d286de4c7f53d541 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 10 May 2026 11:31:03 +0200 Subject: [PATCH 152/697] batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown commit 77098e4bea37af51d3962efa88a5af2ea5e1ac57 upstream. The receiver shutdown timer handler, batadv_tp_receiver_shutdown(), is responsible for releasing the tp_vars reference it holds. However, the existing logic for coordinating this release with batadv_tp_stop_all() was flawed. timer_shutdown_sync() guarantees the timer will not fire again after it returns, but it returns non-zero only when the timer was pending at the time of the call. If the timer had already expired (and batadv_tp_stop_all() would unsucessfully try to rearm itself), batadv_tp_stop_all() skips its batadv_tp_vars_put(), and batadv_tp_receiver_shutdown() fails to put its own reference as well. Fix this by introducing a new atomic variable receiving that is set to 1 when the receiver is initialized and cleared atomically with atomic_xchg() by whichever side claims it first. Only the side that observes the transition from 1 to 0 is responsible for releasing the tp_vars timer reference, eliminating the uncertainty. Cc: stable@kernel.org Fixes: 3d3cf6a7314a ("batman-adv: stop tp_meter sessions during mesh teardown") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 13 +++++++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e0d7f98e5b9d..6a6fcf2d2f00 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -8,6 +8,7 @@ #include "main.h" #include +#include #include #include #include @@ -1157,6 +1158,9 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t) spin_unlock_bh(&tp_vars->unacked_lock); /* drop reference of timer */ + if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1)) + return; + batadv_tp_vars_put(tp_vars); } @@ -1375,6 +1379,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, ether_addr_copy(tp_vars->other_end, icmp->orig); tp_vars->role = BATADV_TP_RECEIVER; + atomic_set(&tp_vars->receiving, 1); memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session)); tp_vars->last_recv = BATADV_TP_FIRST_SEQ; tp_vars->bat_priv = bat_priv; @@ -1547,8 +1552,12 @@ void batadv_tp_stop_all(struct batadv_priv *bat_priv) break; case BATADV_TP_RECEIVER: batadv_tp_list_detach(tp_var); - if (timer_shutdown_sync(&tp_var->timer)) - batadv_tp_vars_put(tp_var); + timer_shutdown_sync(&tp_var->timer); + + if (atomic_xchg(&tp_var->receiving, 0) != 1) + break; + + batadv_tp_vars_put(tp_var); break; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index d9518d73c257..c886d86cba63 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1332,6 +1332,9 @@ struct batadv_tp_vars { /** @sending: sending binary semaphore: 1 if sending, 0 is not */ atomic_t sending; + /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ + atomic_t receiving; + /** @reason: reason for a stopped session */ enum batadv_tp_meter_reason reason; From 72d670d7a4928003dd2e8c1dfedd424a8d4f9804 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Wed, 13 May 2026 23:38:54 +0200 Subject: [PATCH 153/697] batman-adv: tp_meter: fix race condition in send error reporting commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream. batadv_tp_sender_shutdown() previously used two separate variables to track session state: sending (an atomic flag indicating whether the session was active) and reason (a plain enum storing the stop reason). This introduced a race window between the two writes: after sending was cleared to 0, batadv_tp_send() could observe the stopped state and call batadv_tp_sender_end() before reason was written, causing the wrong stop reason to be reported to the caller. Fix this by consolidating both variables into a single atomic send_result, which holds 0 while the session is running and the stop reason once it ends. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 40 ++++++++++++++++++++++++--------------- net/batman-adv/types.h | 10 +++++----- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 6a6fcf2d2f00..e5342760b49f 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars) static void batadv_tp_sender_end(struct batadv_priv *bat_priv, struct batadv_tp_vars *tp_vars) { + enum batadv_tp_meter_reason reason; u32 session_cookie; + reason = atomic_read(&tp_vars->send_result); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Test towards %pM finished..shutting down (reason=%d)\n", - tp_vars->other_end, tp_vars->reason); + tp_vars->other_end, reason); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n", @@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, session_cookie = batadv_tp_session_cookie(tp_vars->session, tp_vars->icmp_uid); - batadv_tp_batctl_notify(tp_vars->reason, + batadv_tp_batctl_notify(reason, tp_vars->other_end, bat_priv, tp_vars->start_time, @@ -446,10 +449,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars, enum batadv_tp_meter_reason reason) { - if (atomic_xchg(&tp_vars->sending, 0) != 1) - return; + atomic_cmpxchg(&tp_vars->send_result, 0, reason); +} - tp_vars->reason = reason; +/** + * batadv_tp_sender_stopped() - check if tp session was stopped with reason + * @tp_vars: the private data of the current TP meter session + * + * Return: whether stop reason was found + */ +static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars) +{ + return atomic_read(&tp_vars->send_result) != 0; } /** @@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars) /* most of the time this function is invoked while normal packet * reception... */ - if (unlikely(atomic_read(&tp_vars->sending) == 0)) + if (unlikely(batadv_tp_sender_stopped(tp_vars))) /* timer ref will be dropped in batadv_tp_sender_cleanup */ return; @@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t) struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer); struct batadv_priv *bat_priv = tp_vars->bat_priv; - if (atomic_read(&tp_vars->sending) == 0) + if (batadv_tp_sender_stopped(tp_vars)) return; /* if the user waited long enough...shutdown the test */ @@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (unlikely(tp_vars->role != BATADV_TP_SENDER)) goto out; - if (unlikely(atomic_read(&tp_vars->sending) == 0)) + if (unlikely(batadv_tp_sender_stopped(tp_vars))) goto out; /* old ACK? silently drop it.. */ @@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg) if (unlikely(tp_vars->role != BATADV_TP_SENDER)) { err = BATADV_TP_REASON_DST_UNREACHABLE; - tp_vars->reason = err; + batadv_tp_sender_shutdown(tp_vars, err); goto out; } orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end); if (unlikely(!orig_node)) { err = BATADV_TP_REASON_DST_UNREACHABLE; - tp_vars->reason = err; + batadv_tp_sender_shutdown(tp_vars, err); goto out; } primary_if = batadv_primary_if_get_selected(bat_priv); if (unlikely(!primary_if)) { err = BATADV_TP_REASON_DST_UNREACHABLE; - tp_vars->reason = err; + batadv_tp_sender_shutdown(tp_vars, err); goto out; } @@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg) queue_delayed_work(batadv_event_workqueue, &tp_vars->finish_work, msecs_to_jiffies(tp_vars->test_length)); - while (atomic_read(&tp_vars->sending) != 0) { + while (!batadv_tp_sender_stopped(tp_vars)) { if (unlikely(!batadv_tp_avail(tp_vars, payload_len))) { batadv_tp_wait_available(tp_vars, payload_len); continue; @@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg) "Meter: %s() cannot send packets (%d)\n", __func__, err); /* ensure nobody else tries to stop the thread now */ - if (atomic_xchg(&tp_vars->sending, 0) == 1) - tp_vars->reason = err; + batadv_tp_sender_shutdown(tp_vars, err); break; } @@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, ether_addr_copy(tp_vars->other_end, dst); kref_init(&tp_vars->refcount); tp_vars->role = BATADV_TP_SENDER; - atomic_set(&tp_vars->sending, 1); + atomic_set(&tp_vars->send_result, 0); memcpy(tp_vars->session, session_id, sizeof(session_id)); tp_vars->icmp_uid = icmp_uid; diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index c886d86cba63..6b4c48bff561 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1329,15 +1329,15 @@ struct batadv_tp_vars { /** @role: receiver/sender modi */ enum batadv_tp_meter_role role; - /** @sending: sending binary semaphore: 1 if sending, 0 is not */ - atomic_t sending; + /** + * @send_result: 0 when sending is ongoing and otherwise + * enum batadv_tp_meter_reason + */ + atomic_t send_result; /** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ atomic_t receiving; - /** @reason: reason for a stopped session */ - enum batadv_tp_meter_reason reason; - /** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work; From 2d2d365d0b9d0987e37c6172f8c4b9f7a2ba569f Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 16 May 2026 12:33:41 +0200 Subject: [PATCH 154/697] batman-adv: tp_meter: avoid role confusion in tp_list commit ff24f2ecfd94c07a2b89bac497433e3b23271cac upstream. Session lookups in tp_list matched only on destination address (and optionally session ID), leaving role validation to the caller. If two sessions with the same other_end coexisted (one as sender, one as receiver) a lookup could silently return the wrong one, causing the caller's role to bail out early, potentially skipping necessary cleanup. Move the role check into the lookup functions themselves so the correct entry is always returned, or none at all. Since batadv_tp_start() legitimately needs to detect any active session to a destination regardless of role, introduce a dedicated helper for that case rather than bending the existing lookup semantics. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/tp_meter.c | 59 ++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e5342760b49f..b1629e0ac826 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -255,6 +255,7 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, * batadv_tp_list_find() - find a tp_vars object in the global list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for + * @role: role of the session * * Look for a tp_vars object matching dst as end_point and return it after * having increment the refcounter. Return NULL is not found @@ -262,7 +263,8 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason, * Return: matching tp_vars or NULL when no tp_vars with @dst was found */ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, - const u8 *dst) + const u8 *dst, + enum batadv_tp_meter_role role) { struct batadv_tp_vars *pos, *tp_vars = NULL; @@ -271,6 +273,9 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, if (!batadv_compare_eth(pos->other_end, dst)) continue; + if (pos->role != role) + continue; + /* most of the time this function is invoked during the normal * process..it makes sens to pay more when the session is * finished and to speed the process up during the measurement @@ -286,12 +291,33 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, return tp_vars; } +/** + * batadv_tp_list_active() - check if session from/to destination is ongoing + * @bat_priv: the bat priv with all the mesh interface information + * @dst: the other endpoint MAC address to look for + * + * Return: if matching session with @dst was found + */ +static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst) + __must_hold(&bat_priv->tp_list_lock) +{ + struct batadv_tp_vars *tp_vars; + + hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) { + if (batadv_compare_eth(tp_vars->other_end, dst)) + return true; + } + + return false; +} + /** * batadv_tp_list_find_session() - find tp_vars session object in the global * list * @bat_priv: the bat priv with all the mesh interface information * @dst: the other endpoint MAC address to look for * @session: session identifier + * @role: role of the session * * Look for a tp_vars object matching dst as end_point, session as tp meter * session and return it after having increment the refcounter. Return NULL @@ -301,7 +327,7 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv, */ static struct batadv_tp_vars * batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, - const u8 *session) + const u8 *session, enum batadv_tp_meter_role role) { struct batadv_tp_vars *pos, *tp_vars = NULL; @@ -313,6 +339,9 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst, if (memcmp(pos->session, session, sizeof(pos->session)) != 0) continue; + if (pos->role != role) + continue; + /* most of the time this function is invoked during the normal * process..it makes sense to pay more when the session is * finished and to speed the process up during the measurement @@ -665,13 +694,10 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, /* find the tp_vars */ tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session); + icmp->session, BATADV_TP_SENDER); if (unlikely(!tp_vars)) return; - if (unlikely(tp_vars->role != BATADV_TP_SENDER)) - goto out; - if (unlikely(batadv_tp_sender_stopped(tp_vars))) goto out; @@ -980,10 +1006,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, return; } - tp_vars = batadv_tp_list_find(bat_priv, dst); - if (tp_vars) { + if (batadv_tp_list_active(bat_priv, dst)) { spin_unlock_bh(&bat_priv->tp_list_lock); - batadv_tp_vars_put(tp_vars); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: test to or from the same node already ongoing, aborting\n"); batadv_tp_batctl_error_notify(BATADV_TP_REASON_ALREADY_ONGOING, @@ -1104,18 +1128,14 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst, if (!orig_node) return; - tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig); + tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER); if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: trying to interrupt an already over connection\n"); goto out_put_orig_node; } - if (unlikely(tp_vars->role != BATADV_TP_SENDER)) - goto out_put_tp_vars; - batadv_tp_sender_shutdown(tp_vars, return_value); -out_put_tp_vars: batadv_tp_vars_put(tp_vars); out_put_orig_node: batadv_orig_node_put(orig_node); @@ -1371,7 +1391,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, goto out_unlock; tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session); + icmp->session, BATADV_TP_RECEIVER); if (tp_vars) goto out_unlock; @@ -1442,7 +1462,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, } } else { tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig, - icmp->session); + icmp->session, BATADV_TP_RECEIVER); if (!tp_vars) { batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Unexpected packet from %pM!\n", @@ -1451,13 +1471,6 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv, } } - if (unlikely(tp_vars->role != BATADV_TP_RECEIVER)) { - batadv_dbg(BATADV_DBG_TP_METER, bat_priv, - "Meter: dropping packet: not expected (role=%u)\n", - tp_vars->role); - goto out; - } - tp_vars->last_recv_time = jiffies; /* if the packet is a duplicate, it may be the case that an ACK has been From 4cc85aec8d3c9ab4dc716dc9f1ed36fca16b227f Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 19:47:11 +0200 Subject: [PATCH 155/697] batman-adv: tt: fix TOCTOU race for reported vlans commit 94d27005016be15ffc638b2ecbc4d58805ad7b48 upstream. The local TT based TVLV is generated by first checking the number of VLANs which have at least one TT entry. A new buffer with the correct size for the VLANs is then allocated. Only then, the list of VLANs s used to fill the VLAN entries in the buffer. During this time, the meshif_vlan_list_lock is held. But the actual number of TT entries of each VLAN can still increase during this time - just not the number of VLANs in the list. But the prefilter used in the buffer size calculation might still cause an increase of the number of VLANs which need to be stored. Simply because a VLAN might now suddenly have at least one entry when it had none in the pre-alloc check - and then needs to occupy space which was not allocated. It is better to overestimate the buffer size at the beginning and then fill the buffer only with the VLANs which are not empty. Cc: stable@kernel.org Fixes: 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 05cddcf994f6..bf5f35a3c9ba 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -887,11 +887,8 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, spin_lock_bh(&bat_priv->meshif_vlan_list_lock); hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); - if (vlan_entries < 1) - continue; - - num_vlan++; total_entries += vlan_entries; + num_vlan++; } change_offset = struct_size(*tt_data, vlan_data, num_vlan); @@ -914,6 +911,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, (*tt_data)->num_vlan = htons(num_vlan); tt_vlan = (*tt_data)->vlan_data; + num_vlan = 0; hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); if (vlan_entries < 1) @@ -924,8 +922,15 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, tt_vlan->reserved = 0; tt_vlan++; + num_vlan++; } + /* recalculate in case number of VLANs reduced */ + change_offset = struct_size(*tt_data, vlan_data, num_vlan); + tvlv_len = *tt_len + change_offset; + + (*tt_data)->num_vlan = htons(num_vlan); + tt_change_ptr = (u8 *)*tt_data + change_offset; *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; From 7cac9c9ef4b7fa3e4959098b9801723fa9f25908 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 19:08:37 +0200 Subject: [PATCH 156/697] batman-adv: tt: reject oversized local TVLV buffers commit 1e9fab756f8395096d5bba7be0c373c4c8f5d165 upstream. The commit 3a359bf5c61d ("batman-adv: reject oversized global TT response buffers") added a check to ensure that a global return buffer size can be stored in an u16. The same buffer handling also exists for the local data buffer but was not touched. A similar check should be also be in place for the local TVLV buffer. It doesn't have the similar attack surface because it is only generated from locally discovered MAC addresses but the dynamic nature could still cause temporarily to large buffers. Cc: stable@kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index bf5f35a3c9ba..f009cbf8a276 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -877,12 +877,12 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, { struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_meshif_vlan *vlan; + size_t change_offset; u16 num_vlan = 0; u16 vlan_entries = 0; u16 total_entries = 0; u16 tvlv_len; u8 *tt_change_ptr; - int change_offset; spin_lock_bh(&bat_priv->meshif_vlan_list_lock); hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { @@ -897,8 +897,10 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, if (*tt_len < 0) *tt_len = batadv_tt_len(total_entries); - tvlv_len = *tt_len; - tvlv_len += change_offset; + if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { + tvlv_len = 0; + goto out; + } *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); if (!*tt_data) { From b93ca6012712ecab2b551e120d7c95038d6a89e5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 20:47:34 +0200 Subject: [PATCH 157/697] batman-adv: tt: avoid empty VLAN responses commit fa1bd704940b5bcbc32c0b28db9167405c8ee5e0 upstream. The commit 16116dac2339 ("batman-adv: prevent TT request storms by not sending inconsistent TT TLVLs") added checks to the local (direct) TT response code. But the response can also be done indirectly by another node using the global TT state. To avoid such inconsistency states reported in the original fix, also avoid sending empty VLANs for replies from the global TT state. Cc: stable@kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index f009cbf8a276..2259b241e0b5 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -797,24 +797,26 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, s32 *tt_len) { u16 num_vlan = 0; - u16 num_entries = 0; u16 tvlv_len = 0; unsigned int change_offset; struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_orig_node_vlan *vlan; + u16 total_entries = 0; u8 *tt_change_ptr; + int vlan_entries; spin_lock_bh(&orig_node->vlan_list_lock); hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + total_entries += vlan_entries; num_vlan++; - num_entries += atomic_read(&vlan->tt.num_entries); } change_offset = struct_size(*tt_data, vlan_data, num_vlan); /* if tt_len is negative, allocate the space needed by the full table */ if (*tt_len < 0) - *tt_len = batadv_tt_len(num_entries); + *tt_len = batadv_tt_len(total_entries); if (change_offset > U16_MAX || *tt_len > U16_MAX - change_offset) { *tt_len = 0; @@ -835,14 +837,26 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, (*tt_data)->num_vlan = htons(num_vlan); tt_vlan = (*tt_data)->vlan_data; + num_vlan = 0; hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { + vlan_entries = atomic_read(&vlan->tt.num_entries); + if (vlan_entries < 1) + continue; + tt_vlan->vid = htons(vlan->vid); tt_vlan->crc = htonl(vlan->tt.crc); tt_vlan->reserved = 0; tt_vlan++; + num_vlan++; } + /* recalculate in case number of VLANs reduced */ + change_offset = struct_size(*tt_data, vlan_data, num_vlan); + tvlv_len = *tt_len + change_offset; + + (*tt_data)->num_vlan = htons(num_vlan); + tt_change_ptr = (u8 *)*tt_data + change_offset; *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; From 179eb62506a02d00370bd6478898cb632e10986c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 19:53:21 +0200 Subject: [PATCH 158/697] batman-adv: tt: fix negative last_changeset_len commit fc92cdfcb295cefa4344d71a527d61b638b7bfc4 upstream. batadv_piv_tt::last_changeset_len len was declared as s16, but the field is never intended to hold a negative value. When a value greater than 32767 is assigned, it wraps to a negative signed integer. In batadv_send_my_tt_response(), last_changeset_len is temporarily widened to s32. The incorrectly negative s16 value propagates into the s32, causing batadv_tt_prepare_tvlv_local_data() to allocate a full sized buffer but populates only a small portion of it with the collected changeset. All remaining bits are kept uninitialized. Using an u16 avoids this type confusion and ensures that no (negative) sign extension is performed in batadv_send_my_tt_response(). Cc: stable@kernel.org Fixes: a73105b8d4c7 ("batman-adv: improved client announcement mechanism") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 6b4c48bff561..71845ca09754 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -996,7 +996,7 @@ struct batadv_priv_tt { * @last_changeset_len: length of last tt changeset this host has * generated */ - s16 last_changeset_len; + u16 last_changeset_len; /** * @last_changeset_lock: lock protecting last_changeset & From 730de8733dd90f70d7580a9b329b971f8e1474a2 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 19:53:21 +0200 Subject: [PATCH 159/697] batman-adv: tt: fix negative tt_buff_len commit b64963a2ceeb7529310b6cf253a1e540784422f4 upstream. batadv_orig_node::tt_buff_len was declared as s16, but the field is never intended to hold a negative value. When a value greater than 32767 is assigned, it wraps to a negative signed integer. In batadv_send_other_tt_response(), tt_buff_len is temporarily widened to s32. The incorrectly negative s16 value propagates into the s32, causing batadv_tt_prepare_tvlv_global_data() to allocate a full sized buffer but populates only a small portion of it with the collected changeset. All remaining bits are kept uninitialized. Using an u16 avoids this type confusion and ensures that no (negative) sign extension is performed in batadv_send_other_tt_response(). Cc: stable@kernel.org Fixes: a73105b8d4c7 ("batman-adv: improved client announcement mechanism") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 71845ca09754..c9bd49d23547 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -455,7 +455,7 @@ struct batadv_orig_node { * @tt_buff_len: length of the last tt changeset this node received * from the orig node */ - s16 tt_buff_len; + u16 tt_buff_len; /** @tt_buff_lock: lock that protects tt_buff and tt_buff_len */ spinlock_t tt_buff_lock; From e37dbe150515ff70113c7117a50c981978f5d3a1 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 2 May 2026 21:25:19 +0200 Subject: [PATCH 160/697] batman-adv: tt: prevent TVLV entry number overflow commit 99d9958fa10fb684b2a8e2c48a8d704122721420 upstream. The helpers to prepare the buffers for the local and global TT based replies are trying to sum up all TT entries which can be found for each VLAN. In theory, this sum can be too big for an u16 and therefore overflow. A too small buffer would then be allocated for the TVLV. The too small buffer will be handled gracefully by batadv_tt_tvlv_generate() and is not causing a buffer overflow - just a truncated reply. But this overflow shouldn't have happened in the first and the too small buffer should never have been allocated when an overflow was detected. Cc: stable@kernel.org Fixes: 7ea7b4a14275 ("batman-adv: make the TT CRC logic VLAN specific") Signed-off-by: Sven Eckelmann Signed-off-by: Greg Kroah-Hartman --- net/batman-adv/translation-table.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 2259b241e0b5..9f6e67771ffa 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -804,11 +804,18 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, u16 total_entries = 0; u8 *tt_change_ptr; int vlan_entries; + u16 sum_entries; spin_lock_bh(&orig_node->vlan_list_lock); hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); - total_entries += vlan_entries; + + if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { + *tt_len = 0; + goto out; + } + + total_entries = sum_entries; num_vlan++; } @@ -893,15 +900,22 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, struct batadv_meshif_vlan *vlan; size_t change_offset; u16 num_vlan = 0; - u16 vlan_entries = 0; u16 total_entries = 0; u16 tvlv_len; u8 *tt_change_ptr; + int vlan_entries; + u16 sum_entries; spin_lock_bh(&bat_priv->meshif_vlan_list_lock); hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); - total_entries += vlan_entries; + + if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { + tvlv_len = 0; + goto out; + } + + total_entries = sum_entries; num_vlan++; } From e9b8f85daebffc34506c4705e34eb0f152ac88bf Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Fri, 15 May 2026 15:11:47 -0700 Subject: [PATCH 161/697] hwmon: (pmbus/adm1266) seed timestamp from the real-time clock commit b86095e3d7dcf2bf80c747349a35912a87a85098 upstream. adm1266_set_rtc() seeds the chip's SET_RTC register from ktime_get_seconds(), which returns CLOCK_MONOTONIC -- i.e. seconds since the host last booted, not seconds since the Unix epoch. The chip stamps that value into every blackbox record it captures. Userspace reading those timestamps back expects wall-clock seconds: that's what the SET_RTC frame layout documents (datasheet Rev. D, Table 84) and what every other consumer of "seconds since epoch" assumes. Seeding from CLOCK_MONOTONIC gives blackbox records a timestamp that is only meaningful within a single boot of the host and silently resets to small values on every reboot. Switch to ktime_get_real_seconds() so the seed matches what the register is documented to hold. Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-1-1c1ea1349cfe@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 5348feefd1e6..94691dec1359 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -432,7 +432,7 @@ static int adm1266_set_rtc(struct adm1266_data *data) char write_buf[6]; int i; - kt = ktime_get_seconds(); + kt = ktime_get_real_seconds(); memset(write_buf, 0, sizeof(write_buf)); From 75c862adf3d3caab4f49bb3530723c215376e37c Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Fri, 15 May 2026 15:11:49 -0700 Subject: [PATCH 162/697] hwmon: (pmbus/adm1266) reject implausible blackbox record_count commit 4afca954622d672ea65ed961bed01cf91caa034e upstream. adm1266_nvmem_read_blackbox() loops over a record_count that comes straight from byte 3 of the BLACKBOX_INFO response. The destination buffer is data->dev_mem, sized for the nvmem cell's declared 2048 bytes (ADM1266_BLACKBOX_MAX_RECORDS * ADM1266_BLACKBOX_SIZE = 32 * 64). A device that reports a record_count greater than 32 -- whether due to firmware bugs, bus corruption, or a non-responsive slave returning 0xff -- would walk read_buff past the end of the dev_mem allocation on the trailing iterations. Cap record_count at ADM1266_BLACKBOX_MAX_RECORDS (introduced here) before entering the loop and return -EIO on any larger value, so a malformed BLACKBOX_INFO response cannot drive the loop out of bounds. Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-3-1c1ea1349cfe@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 94691dec1359..43d9e7407795 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -46,6 +46,7 @@ #define ADM1266_BLACKBOX_OFFSET 0 #define ADM1266_BLACKBOX_SIZE 64 +#define ADM1266_BLACKBOX_MAX_RECORDS 32 #define ADM1266_PMBUS_BLOCK_MAX 255 @@ -360,6 +361,8 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) return -EIO; record_count = buf[3]; + if (record_count > ADM1266_BLACKBOX_MAX_RECORDS) + return -EIO; for (index = 0; index < record_count; index++) { ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff); From 2279c342d94eca225bf9f301c8806a05a1c81619 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Fri, 15 May 2026 15:11:50 -0700 Subject: [PATCH 163/697] hwmon: (pmbus/adm1266) include PEC byte in pmbus_block_xfer read buffer commit 487566cb1ccdf3756fdd7bf8d875e612ff3169bb upstream. adm1266_pmbus_block_xfer() sets up the read transaction with .buf = data->read_buf, .len = ADM1266_PMBUS_BLOCK_MAX + 2, but read_buf in struct adm1266_data is declared as u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1]; For a max-length block response (length byte = 255 + up to 1 PEC byte), the i2c controller is told to write 257 bytes into a 256-byte buffer, putting one byte past the end of read_buf. The same response also makes the subsequent PEC compare if (crc != msgs[1].buf[msgs[1].buf[0] + 1]) read a byte beyond the array. Bump the read_buf declaration to ADM1266_PMBUS_BLOCK_MAX + 2 so the buffer can hold the length byte, up to 255 payload bytes, and the PEC byte the i2c_msg length already accounts for. Fixes: 407dc802a9c0 ("hwmon: (pmbus/adm1266) Add Block process call") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-4-1c1ea1349cfe@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 43d9e7407795..5c68e3177f64 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -61,7 +61,7 @@ struct adm1266_data { u8 *dev_mem; struct mutex buf_mutex; u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned; - u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned; + u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 2] ____cacheline_aligned; }; static const struct nvmem_cell_info adm1266_nvmem_cells[] = { From 97a9cf2a8217ca1cdaf48cb9ab26e471632c7e7f Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Fri, 15 May 2026 15:11:51 -0700 Subject: [PATCH 164/697] hwmon: (pmbus/adm1266) bounce blackbox records through a protocol-sized buffer commit 43cae21424ff8e33894a0f86c6b80b840c049fd7 upstream. adm1266_pmbus_block_xfer() copies the device-supplied block payload into the caller-provided buffer using the device-supplied length: memcpy(data_r, &msgs[1].buf[1], msgs[1].buf[0]); The helper does not know how large data_r is and trusts the device to return at most one record's worth of bytes. adm1266_nvmem_read_blackbox() violates that contract: it advances read_buff inside data->dev_mem in ADM1266_BLACKBOX_SIZE (64-byte) strides while the helper is willing to write up to ADM1266_PMBUS_BLOCK_MAX (255) bytes. A device that returns more than 64 bytes on the trailing record (read_buff offset 1984 in the 2048-byte dev_mem allocation) overflows dev_mem by up to 191 bytes before the post-call if (ret != ADM1266_BLACKBOX_SIZE) return -EIO; can reject the response. Contain the fix in the caller without changing the helper signature: read each record into a 255-byte local bounce buffer that matches the helper's maximum output, validate the returned length, and only then copy exactly ADM1266_BLACKBOX_SIZE bytes into the dev_mem slot. Fixes: 407dc802a9c0 ("hwmon: (pmbus/adm1266) Add Block process call") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260515-adm1266-fixes-v1-5-1c1ea1349cfe@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 5c68e3177f64..ea1edb89d2a0 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -348,6 +348,7 @@ static void adm1266_init_debugfs(struct adm1266_data *data) static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) { + u8 record[ADM1266_PMBUS_BLOCK_MAX]; int record_count; char index; u8 buf[I2C_SMBUS_BLOCK_MAX]; @@ -365,13 +366,14 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff) return -EIO; for (index = 0; index < record_count; index++) { - ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff); + ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, record); if (ret < 0) return ret; if (ret != ADM1266_BLACKBOX_SIZE) return -EIO; + memcpy(read_buff, record, ADM1266_BLACKBOX_SIZE); read_buff += ADM1266_BLACKBOX_SIZE; } From fa7ca363069a70b0d1aa51e8892e3095fe2ac1ec Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 18 May 2026 17:52:25 -0700 Subject: [PATCH 165/697] hwmon: (pmbus/adm1266) cap PDIO scan in get_multiple at ADM1266_PDIO_NR commit d7834d92251baade796812876e95555e2066fa9f upstream. adm1266_gpio_get_multiple() iterates the PDIO portion of the caller-supplied mask using for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) { ... } where ADM1266_PDIO_STATUS is the PMBus command code (0xE9, i.e. 233), not the number of PDIO pins. The intended upper bound is ADM1266_GPIO_NR + ADM1266_PDIO_NR = 25. gpiolib hands in a mask sized for gc.ngpio (= 25 bits on this chip), so the iteration walks find_next_bit() up to 242, reading up to 217 extra bits (a handful of unsigned-long words: four on 64-bit, seven on 32-bit) of whatever lives past the end of the mask in the caller's stack. Any incidental set bit in that range then drives a set_bit(gpio_nr, bits) call that writes past the end of the caller-supplied bits array too -- both out-of-bounds. Substitute ADM1266_PDIO_NR for the constant so the scan stops at the last real PDIO bit. Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-1-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index ea1edb89d2a0..0e2e80bf6c8c 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -212,7 +212,7 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask status = read_buf[0] + (read_buf[1] << 8); *bits = 0; - for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) { + for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) { if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status)) set_bit(gpio_nr, bits); } From b2998ae903315af553d4860312d5189084eeedd9 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 18 May 2026 17:52:26 -0700 Subject: [PATCH 166/697] hwmon: (pmbus/adm1266) don't clobber GPIO bits before PDIO read in get_multiple commit 3327a12aee9e10ffa903e28b8445dfd1af5307c0 upstream. adm1266_gpio_get_multiple() zeroes *bits before the GPIO_STATUS loop and then a second time before the PDIO_STATUS loop: *bits = 0; for_each_set_bit(gpio_nr, mask, ADM1266_GPIO_NR) { ... set_bit(gpio_nr, bits); } ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, ...); ... *bits = 0; for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) { ... set_bit(gpio_nr, bits); } The second *bits = 0 throws away every GPIO bit the first loop just populated, so callers asking for any combination of GPIO and PDIO pins always see the GPIO portion of the returned bits as zero. Drop the redundant second assignment so both halves of the result survive. Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Reviewed-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-2-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 0e2e80bf6c8c..0c8d49dd3ef1 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -211,7 +211,6 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask status = read_buf[0] + (read_buf[1] << 8); - *bits = 0; for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) { if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status)) set_bit(gpio_nr, bits); From a203125c0e818809b206f269a6386c827c3286ff Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 18 May 2026 17:52:28 -0700 Subject: [PATCH 167/697] hwmon: (pmbus/adm1266) register the gpio_chip after pmbus_do_probe() commit 491403b9b76cf66abd81301c5901aa4a4549f1e8 upstream. adm1266_probe() calls adm1266_config_gpio() -- which goes on to devm_gpiochip_add_data() and exposes the gpio_chip callbacks to gpiolib -- before pmbus_do_probe() has initialised the per-client PMBus state (notably the pmbus_lock mutex the core hands out via pmbus_get_data()). That ordering is already a latent hazard: any GPIO access that lands between adm1266_config_gpio() and the end of pmbus_do_probe() (for example a sysfs read from a user space agent that opens the gpiochip the instant gpiolib advertises it) races pmbus_do_probe()'s own device accesses with no serialisation. Move adm1266_config_gpio() down past pmbus_do_probe() so the chip isn't reachable from userspace until the PMBus state it depends on is fully initialised. Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-4-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 0c8d49dd3ef1..575a6642e850 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -466,10 +466,6 @@ static int adm1266_probe(struct i2c_client *client) crc8_populate_msb(pmbus_crc_table, 0x7); mutex_init(&data->buf_mutex); - ret = adm1266_config_gpio(data); - if (ret < 0) - return ret; - ret = adm1266_set_rtc(data); if (ret < 0) return ret; @@ -482,6 +478,10 @@ static int adm1266_probe(struct i2c_client *client) if (ret) return ret; + ret = adm1266_config_gpio(data); + if (ret < 0) + return ret; + adm1266_init_debugfs(data); return 0; From dd12c6dbe2ac1c16e4d86af7b8a5a3858977c54c Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 18 May 2026 17:52:29 -0700 Subject: [PATCH 168/697] hwmon: (pmbus/adm1266) register the nvmem device after pmbus_do_probe() commit 6af713af91d5c34ec049eb3cc2c5b3f5eba953b8 upstream. adm1266_probe() calls adm1266_config_nvmem() -- which goes on to devm_nvmem_register() and exposes adm1266_nvmem_read() to userspace -- before pmbus_do_probe() has initialised the per-client PMBus state. Same latent hazard as the gpio_chip one fixed in the previous patch: once the nvmem device is registered, gpiolib's nvmem char-dev / sysfs interface is reachable, and any concurrent read triggers adm1266_nvmem_read() -> adm1266_nvmem_read_blackbox(), which issues PMBus traffic that races pmbus_do_probe()'s own device accesses with no serialisation. Move adm1266_config_nvmem() down past pmbus_do_probe() so the nvmem device isn't reachable from userspace until the PMBus state the nvmem accessors depend on is fully initialised. Fixes: 15609d189302 ("hwmon: (pmbus/adm1266) read blackbox") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-5-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 575a6642e850..8aa52215cff7 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -470,14 +470,14 @@ static int adm1266_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = adm1266_config_nvmem(data); - if (ret < 0) - return ret; - ret = pmbus_do_probe(client, &data->info); if (ret) return ret; + ret = adm1266_config_nvmem(data); + if (ret < 0) + return ret; + ret = adm1266_config_gpio(data); if (ret < 0) return ret; From eb3cd9bb590460c6127145cb245be925d23f5232 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 18 May 2026 17:52:27 -0700 Subject: [PATCH 169/697] hwmon: (pmbus/adm1266) reject short block-read responses in the GPIO accessors commit a7232f68c43ca62f545049b7f5fbfc75137b843b upstream. adm1266_gpio_get() and adm1266_gpio_get_multiple() both compose the pin-status word as pins_status = read_buf[0] + (read_buf[1] << 8); right after i2c_smbus_read_block_data(), guarding only against an error return. A well-behaved device returns 2 bytes for GPIO_STATUS/PDIO_STATUS, but the helper happily reports a 0- or 1-byte response too. If the device returns 0 bytes, both read_buf slots are uninitialized stack memory; if it returns 1 byte, read_buf[1] is. The composed value then flows through set_bit() into the caller's *bits in adm1266_gpio_get_multiple(), or into the return value of adm1266_gpio_get(), and ends up in userspace via gpiolib (sysfs and the char-dev ioctls). That leaks a few bits of kernel stack per request on any device whose firmware glitch, bus error, or hostile slave produces a short block-read response. Add the missing length check to both call sites and surface a short response as -EIO. Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-3-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 8aa52215cff7..9631a64cb1eb 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -176,6 +176,8 @@ static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset) ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf); if (ret < 0) return ret; + if (ret < 2) + return -EIO; pins_status = read_buf[0] + (read_buf[1] << 8); if (offset < ADM1266_GPIO_NR) @@ -196,6 +198,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf); if (ret < 0) return ret; + if (ret < 2) + return -EIO; status = read_buf[0] + (read_buf[1] << 8); @@ -208,6 +212,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf); if (ret < 0) return ret; + if (ret < 2) + return -EIO; status = read_buf[0] + (read_buf[1] << 8); From 00aca89f5e3453b30b73e31aa31099b1433f8370 Mon Sep 17 00:00:00 2001 From: Til Kaiser Date: Mon, 13 Apr 2026 15:52:34 +0200 Subject: [PATCH 170/697] pinctrl: qcom: ipq4019: mark gpio as a GPIO pin function [ Upstream commit b51d33ea8a164bb5f0eec8ad817fa9730ac2b577 ] The qcom pinctrl core supports marking functions that represent GPIO mode via PINCTRL_GPIO_PINFUNCTION(), so that strict pinmuxing does not reject GPIO requests for pins that are muxed to the GPIO function. ipq4019 still describes its gpio function with QCA_PIN_FUNCTION(gpio), so it is not treated as a GPIO pin function. As a result, GPIO consumers can still conflict with pinctrl states that select the "gpio" function. Add a QCA_GPIO_PIN_FUNCTION() helper and use it for the ipq4019 gpio function, matching how the msm-based qcom drivers handle this. This allows ipq4019 to keep the GPIO-related pin configuration in DTS without tripping over strict pinmux ownership checks. Fixes: cc85cb96e2e4 ("pinctrl: qcom: make the pinmuxing strict") Signed-off-by: Til Kaiser Reviewed-by: Dmitry Baryshkov Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/pinctrl-ipq4019.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-ipq4019.c b/drivers/pinctrl/qcom/pinctrl-ipq4019.c index 6ede3149b6e1..07df812fb728 100644 --- a/drivers/pinctrl/qcom/pinctrl-ipq4019.c +++ b/drivers/pinctrl/qcom/pinctrl-ipq4019.c @@ -480,7 +480,7 @@ static const struct pinfunction ipq4019_functions[] = { QCA_PIN_FUNCTION(blsp_uart0), QCA_PIN_FUNCTION(blsp_uart1), QCA_PIN_FUNCTION(chip_rst), - QCA_PIN_FUNCTION(gpio), + QCA_GPIO_PIN_FUNCTION(gpio), QCA_PIN_FUNCTION(i2s_rx), QCA_PIN_FUNCTION(i2s_spdif_in), QCA_PIN_FUNCTION(i2s_spdif_out), diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 4625fa5320a9..120217012a9f 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -39,6 +39,11 @@ struct pinctrl_pin_desc; fname##_groups, \ ARRAY_SIZE(fname##_groups)) +#define QCA_GPIO_PIN_FUNCTION(fname) \ + [qca_mux_##fname] = PINCTRL_GPIO_PINFUNCTION(#fname, \ + fname##_groups, \ + ARRAY_SIZE(fname##_groups)) + /** * struct msm_pingroup - Qualcomm pingroup definition * @grp: Generic data of the pin group (name and pins) From d27b29e474a6274cf34eb3afb59e137025ced83c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 28 Mar 2026 00:42:10 +0100 Subject: [PATCH 171/697] ARM: dts: renesas: genmai: Drop superfluous cells [ Upstream commit 714e1d6bba0e0abe5c87c8e189a35fa690540df4 ] Drop superfluous address-cells and size-cells to fix DTC W=1 warning: arch/arm/boot/dts/renesas/r7s72100-genmai.dts:28.17-55.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property Signed-off-by: Marek Vasut Fixes: 30e0a8cf886cb459 ("ARM: dts: renesas: genmai: Add FLASH nodes") Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260327234244.91707-6-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- arch/arm/boot/dts/renesas/r7s72100-genmai.dts | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts index 3c3756509714..da552a66615e 100644 --- a/arch/arm/boot/dts/renesas/r7s72100-genmai.dts +++ b/arch/arm/boot/dts/renesas/r7s72100-genmai.dts @@ -34,9 +34,6 @@ clocks = <&mstp9_clks R7S72100_CLK_SPIBSC0>; power-domains = <&cpg_clocks>; - #address-cells = <1>; - #size-cells = <1>; - partitions { compatible = "fixed-partitions"; #address-cells = <1>; From a7fee132268376ac706605a4526bda60e6e1d7ee Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sat, 28 Mar 2026 00:42:11 +0100 Subject: [PATCH 172/697] ARM: dts: renesas: rskrza1: Drop superfluous cells [ Upstream commit ab83176d3cf1cf1c1f6e604432905bda4515d17f ] Drop superfluous address-cells and size-cells to fix DTC W=1 warning: arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts:32.17-72.4: Warning (avoid_unnecessary_addr_size): /flash@18000000: unnecessary #address-cells/#size-cells without "ranges", "dma-ranges" or child "reg" or "ranges" property Signed-off-by: Marek Vasut Fixes: 98537eb77d3ef185 ("ARM: dts: renesas: rskrza1: Add FLASH nodes") Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260327234244.91707-7-marek.vasut+renesas@mailbox.org Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts index 91178fb9e721..3306bc9b7bc3 100644 --- a/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts +++ b/arch/arm/boot/dts/renesas/r7s72100-rskrza1.dts @@ -36,8 +36,6 @@ power-domains = <&cpg_clocks>; bank-width = <4>; device-width = <1>; - #address-cells = <1>; - #size-cells = <1>; partitions { compatible = "fixed-partitions"; From c4cfa8ee77374630017b9cbd20ba2f6f46c08084 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Sat, 28 Mar 2026 09:05:45 +0000 Subject: [PATCH 173/697] pinctrl: renesas: rzg2l: Fix incorrect PUPD register offset for high pins during suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6dba9b7268cc50166bce47608670192fd874e363 ] When saving/restoring pull-up/down register state during suspend/resume, the second PUPD register access was incorrectly using the same base offset as the first, effectively reading/writing the same register twice instead of the adjacent one. Add the correct + 4 byte offset to the second RZG2L_PCTRL_REG_ACCESS32 call so that pupd[1][port] is properly saved and restored from the next 32-bit register in the PUPD register pair, covering pins 4–7 of ports with 4 or more pins. Fixes: b2bd65fbb617 ("pinctrl: renesas: rzg2l: Add suspend/resume support for pull up/down") Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260328090548.84124-1-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index ca360185740a..df784e3807af 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -3035,7 +3035,7 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), cache->pupd[0][port]); if (pincnt >= 4) { - RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off), + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + PUPD(off) + 4, cache->pupd[1][port]); } } From 8d1c6b603327b22f151de7fbeb7f9f64bec31ccf Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 13 Apr 2026 19:24:51 +0100 Subject: [PATCH 174/697] pinctrl: renesas: rzg2l: Fix SMT register cache handling [ Upstream commit c88ab9407986836820848128ce1f90f2fa49da95 ] Store SMT register cache per bank instead of using a single array. On RZ/V2H(P), RZ/V2N, and RZ/G3E, the SMT register is split across two 32-bit registers: bits 0/8/16/24 control pins 0-3, while pins 4-7 are controlled by the corresponding bits in the next register. The previous implementation cached only a single SMT register, leading to incomplete save/restore of SMT state. Convert cache->smt to a per-bank array and allocate storage for both halves. Update suspend/resume handling to save and restore both SMT registers when present. Fixes: 837afa592c623 ("pinctrl: renesas: rzg2l: Add suspend/resume support for Schmitt control registers") Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260413182456.811543-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Geert Uytterhoeven Signed-off-by: Sasha Levin --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index df784e3807af..aaa783c79e6d 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -335,7 +335,7 @@ struct rzg2l_pinctrl_reg_cache { u32 *iolh[2]; u32 *ien[2]; u32 *pupd[2]; - u32 *smt; + u32 *smt[2]; u8 sd_ch[2]; u8 eth_poc[2]; u8 oen; @@ -2723,10 +2723,6 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) if (!cache->pfc) return -ENOMEM; - cache->smt = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt), GFP_KERNEL); - if (!cache->smt) - return -ENOMEM; - for (u8 i = 0; i < 2; i++) { u32 n_dedicated_pins = pctrl->data->n_dedicated_pins; @@ -2745,6 +2741,11 @@ static int rzg2l_pinctrl_reg_cache_alloc(struct rzg2l_pinctrl *pctrl) if (!cache->pupd[i]) return -ENOMEM; + cache->smt[i] = devm_kcalloc(pctrl->dev, nports, sizeof(*cache->smt[i]), + GFP_KERNEL); + if (!cache->smt[i]) + return -ENOMEM; + /* Allocate dedicated cache. */ dedicated_cache->iolh[i] = devm_kcalloc(pctrl->dev, n_dedicated_pins, sizeof(*dedicated_cache->iolh[i]), @@ -3052,8 +3053,14 @@ static void rzg2l_pinctrl_pm_setup_regs(struct rzg2l_pinctrl *pctrl, bool suspen } } - if (has_smt) - RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), cache->smt[port]); + if (has_smt) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off), + cache->smt[0][port]); + if (pincnt >= 4) { + RZG2L_PCTRL_REG_ACCESS32(suspend, pctrl->base + SMT(off) + 4, + cache->smt[1][port]); + } + } } } From e917713f013423069782ff554935c7a5d4266783 Mon Sep 17 00:00:00 2001 From: Xianwei Zhao Date: Wed, 22 Apr 2026 11:44:13 +0000 Subject: [PATCH 175/697] pinctrl: meson: amlogic-a4: fix deadlock issue [ Upstream commit e72ce029810390eb987a036fb2c8a5da9a23b685 ] Accessing the pinconf-pins sysfs node may deadlock. pinconf_pins_show() holds pctldev->mutex, and the platform driver calls pinctrl_find_gpio_range_from_pin(), which tries to acquire the same mutex again, leading to a deadlock. Use pinctrl_find_gpio_range_from_pin_nolock() to fix this issue. Fixes: 6e9be3abb78c ("pinctrl: Add driver support for Amlogic SoCs") Signed-off-by: Xianwei Zhao Reviewed-by: Neil Armstrong Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index e2293a872dcb..35d27626a336 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -292,7 +292,7 @@ static int aml_calc_reg_and_bit(struct pinctrl_gpio_range *range, static int aml_pinconf_get_pull(struct aml_pinctrl *info, unsigned int pin) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit, val; int ret, conf; @@ -326,7 +326,7 @@ static int aml_pinconf_get_drive_strength(struct aml_pinctrl *info, u16 *drive_strength_ua) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit; unsigned int val; @@ -365,7 +365,7 @@ static int aml_pinconf_get_gpio_bit(struct aml_pinctrl *info, unsigned int reg_type) { struct pinctrl_gpio_range *range = - pinctrl_find_gpio_range_from_pin(info->pctl, pin); + pinctrl_find_gpio_range_from_pin_nolock(info->pctl, pin); struct aml_gpio_bank *bank = gpio_chip_to_bank(range->gc); unsigned int reg, bit, val; int ret; From 1fce9dcb3a662059011b4ce341a9cfb947bf2e1d Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 23 Apr 2026 16:55:24 +0530 Subject: [PATCH 176/697] pinctrl: qcom: Fix GPIO to PDC wake irq map for qcs615 [ Upstream commit 9d69033ad967b6e09b1e5b30d1a32c6c4876465d ] PDC interrupts 122-125 were meant for ibi_i3c wakeup but qcs615 do not support i3c. GPIOs 39,51,88 and 89 are also connected to different PDC pin to support non-ibi wakeup. Update the wakeirq map to reflect same. Fixes: b698f36a9d40 ("pinctrl: qcom: add the tlmm driver for QCS615 platform") Signed-off-by: Maulik Shah Signed-off-by: Navya Malempati Reviewed-by: Konrad Dybcio Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/pinctrl-qcs615.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-qcs615.c b/drivers/pinctrl/qcom/pinctrl-qcs615.c index f1c827ddbfbf..4d474c312c10 100644 --- a/drivers/pinctrl/qcom/pinctrl-qcs615.c +++ b/drivers/pinctrl/qcom/pinctrl-qcs615.c @@ -1043,11 +1043,11 @@ static const struct msm_pingroup qcs615_groups[] = { static const struct msm_gpio_wakeirq_map qcs615_pdc_map[] = { { 1, 45 }, { 3, 31 }, { 7, 55 }, { 9, 110 }, { 11, 34 }, { 13, 33 }, { 14, 35 }, { 17, 46 }, { 19, 48 }, { 21, 83 }, - { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 125 }, { 41, 47 }, - { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 123 }, { 55, 56 }, + { 22, 36 }, { 26, 38 }, { 35, 37 }, { 39, 118 }, { 41, 47 }, + { 47, 49 }, { 48, 51 }, { 50, 52 }, { 51, 116 }, { 55, 56 }, { 56, 57 }, { 57, 58 }, { 60, 60 }, { 71, 54 }, { 80, 73 }, { 81, 64 }, { 82, 50 }, { 83, 65 }, { 84, 92 }, { 85, 99 }, - { 86, 67 }, { 87, 84 }, { 88, 124 }, { 89, 122 }, { 90, 69 }, + { 86, 67 }, { 87, 84 }, { 88, 117 }, { 89, 115 }, { 90, 69 }, { 92, 88 }, { 93, 75 }, { 94, 91 }, { 95, 72 }, { 96, 82 }, { 97, 74 }, { 98, 95 }, { 99, 94 }, { 100, 100 }, { 101, 40 }, { 102, 93 }, { 103, 77 }, { 104, 78 }, { 105, 96 }, { 107, 97 }, From e912d5dc00965e69fe95554769b2032ab728d212 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 23 Apr 2026 10:10:02 +0300 Subject: [PATCH 177/697] HID: intel-thc-hid: Intel-quickspi: Fix some error codes [ Upstream commit ae4ac077332ea3341a0f4c0973556c6b7ac5b7a1 ] If we have a partial read that is supposed to be treated as failure but in this code we forgot to set the error code. Return -EINVAL. Fixes: 9d8d51735a3a ("HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation") Signed-off-by: Dan Carpenter Reviewed-by: Even Xu Reviewed-by: Mark Pearson Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c index 16f780bc879b..cb19057f1191 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -94,7 +94,7 @@ static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", input_len, read_len); - return ret; + return ret ?: -EINVAL; } input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; @@ -318,7 +318,7 @@ int reset_tic(struct quickspi_device *qsdev) dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", read_len, actual_read_len); - return ret; + return ret ?: -EINVAL; } input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); From 4894847fcec140636c1a8f9c719a9652ef1c1b6c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 28 Apr 2026 10:33:16 +0200 Subject: [PATCH 178/697] HID: uclogic: Fix regression of input name assignment [ Upstream commit 487359284509a6745e14b8c0518768bc277809b0 ] The previous fix for adding the devm_kasprintf() return check in the commit bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") changed the condition of hi->input->name assignment, and it resulted in missing the proper input device name when no custom suffix is defined. Restore the conditional to the original content to address the regression. Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()") Signed-off-by: Takashi Iwai Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-uclogic-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index 34fb03ae8ee2..c6db3e7c5fd3 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -184,7 +184,9 @@ static int uclogic_input_configured(struct hid_device *hdev, suffix = "System Control"; break; } - } else { + } + + if (suffix) { hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s %s", hdev->name, suffix); if (!hi->input->name) From 820245d86ce58898fb48b4fefc77d0cafc02801d Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:25 +0100 Subject: [PATCH 179/697] firmware: arm_ffa: Check for NULL FF-A ID table while driver registration [ Upstream commit 0a5e695095c557d2380131b613dea4e8d90371be ] The bus match callback assumes that every FF-A driver provides an id_table and dereferences it unconditionally. Enforce that contract at registration time so a buggy client driver cannot crash the bus during match. Fixes: 92743071464f ("firmware: arm_ffa: Ensure drivers provide a probe function") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-1-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/bus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c index 50bfe56c755e..6779923e35c5 100644 --- a/drivers/firmware/arm_ffa/bus.c +++ b/drivers/firmware/arm_ffa/bus.c @@ -26,6 +26,8 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv) id_table = to_ffa_driver(drv)->id_table; ffa_dev = to_ffa_dev(dev); + if (!id_table) + return 0; while (!uuid_is_null(&id_table->uuid)) { /* @@ -123,7 +125,7 @@ int ffa_driver_register(struct ffa_driver *driver, struct module *owner, { int ret; - if (!driver->probe) + if (!driver->probe || !driver->id_table) return -EINVAL; driver->driver.bus = &ffa_bus_type; From 1418765d28ab11efd1abbee8b691d2a3e50fd20e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:26 +0100 Subject: [PATCH 180/697] firmware: arm_ffa: Skip free_pages on RX buffer alloc failure [ Upstream commit 09527e2c534911619d7e098729711100290bc3e1 ] If the RX buffer allocation fails in ffa_init(), the error path jumps to free_pages even though no buffer has been allocated yet. Route that case directly to free_drv_info so the cleanup path is only used after at least one RX/TX buffer allocation has succeeded. Fixes: 3bbfe9871005 ("firmware: arm_ffa: Add initial Arm FFA driver support") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-2-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index d71a2ef335d1..cf7f913a55b6 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -2063,7 +2063,7 @@ static int __init ffa_init(void) drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); if (!drv_info->rx_buffer) { ret = -ENOMEM; - goto free_pages; + goto free_drv_info; } drv_info->tx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); From 07907b897bb72c580bc6432b8fd652e8f2d47857 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:28 +0100 Subject: [PATCH 181/697] firmware: arm_ffa: Fix per-vcpu self notifications handling in workqueue [ Upstream commit 9985d5357ed93af0d1933969c247e966957730e1 ] Per-vcpu notification handling already runs from a per-cpu work item on the target cpu. Routing that path back through smp_call_function_single() re-enters the call-function IPI path and executes the notification handler with interrupts disabled. That makes the framework path unsafe, since it takes a mutex, allocates memory with GFP_KERNEL, and invokes client callbacks. Handle per-vcpu self notifications directly from the existing per-cpu work item instead. This keeps the per-vcpu path in task context and avoids the extra IPI hop entirely. Fixes: 3a3e2b83e805 ("firmware: arm_ffa: Avoid queuing work when running on the worker queue") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-4-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index cf7f913a55b6..30f8e0ff694f 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1538,7 +1538,7 @@ static void notif_pcpu_irq_work_fn(struct work_struct *work) struct ffa_drv_info *info = container_of(work, struct ffa_drv_info, notif_pcpu_work); - ffa_self_notif_handle(smp_processor_id(), true, info); + notif_get_and_handle(info); } static const struct ffa_info_ops ffa_drv_info_ops = { From 1aa01b46fe3ba0d36c2d0bac382a62a740b6eb80 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:29 +0100 Subject: [PATCH 182/697] firmware: arm_ffa: Unregister bus notifier on teardown for FF-A v1.0 [ Upstream commit 6d3daa9b8d313f42d52e75590310f26a29b61b44 ] For FF-A v1.0 the driver registers a bus notifier to backfill UUID matching, but the notifier was never unregistered on cleanup paths. Track the registration state and unregister it during teardown and early partition-setup failure. Fixes: 9dd15934f60d ("firmware: arm_ffa: Move the FF-A v1.0 NULL UUID workaround to bus notifier") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-5-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 30f8e0ff694f..287db8528610 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -100,6 +100,7 @@ struct ffa_drv_info { bool mem_ops_native; bool msg_direct_req2_supp; bool bitmap_created; + bool bus_notifier_registered; bool notif_enabled; unsigned int sched_recv_irq; unsigned int notif_pend_irq; @@ -1625,6 +1626,15 @@ static struct notifier_block ffa_bus_nb = { .notifier_call = ffa_bus_notifier, }; +static void ffa_bus_notifier_unregister(void) +{ + if (!drv_info->bus_notifier_registered) + return; + + bus_unregister_notifier(&ffa_bus_type, &ffa_bus_nb); + drv_info->bus_notifier_registered = false; +} + static int ffa_xa_add_partition_info(struct ffa_device *dev) { struct ffa_dev_part_info *info; @@ -1708,6 +1718,8 @@ static void ffa_partitions_cleanup(void) struct list_head *phead; unsigned long idx; + ffa_bus_notifier_unregister(); + /* Clean up/free all registered devices */ ffa_devices_unregister(); @@ -1735,11 +1747,14 @@ static int ffa_setup_partitions(void) ret = bus_register_notifier(&ffa_bus_type, &ffa_bus_nb); if (ret) pr_err("Failed to register FF-A bus notifiers\n"); + else + drv_info->bus_notifier_registered = true; } count = ffa_partition_probe(&uuid_null, &pbuf); if (count <= 0) { pr_info("%s: No partitions found, error %d\n", __func__, count); + ffa_bus_notifier_unregister(); return -EINVAL; } From f3216d930c0fa99ed6bbfb1e13c193cee0bea788 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 9 Apr 2026 09:11:39 +0000 Subject: [PATCH 183/697] riscv: errata: Fix bitwise vs logical AND in MIPS errata patching [ Upstream commit 4d2b03699460b8fd5df34408a03a84a1a7ff8aa1 ] The condition checking whether a specific errata needs patching uses logical AND (&&) instead of bitwise AND (&). Since logical AND only checks that both operands are non-zero, this causes all errata patches to be applied whenever any single errata is detected, rather than only applying the matching one. The SiFive errata implementation correctly uses bitwise AND for the same check. Fixes: 0b0ca959d206 ("riscv: errata: Fix the PAUSE Opcode for MIPS P8700") Signed-off-by: Michael Neuling Assisted-by: Cursor:claude-4.6-opus-high-thinking Link: https://patch.msgid.link/20260409091143.1348853-2-mikey@neuling.org [pjw@kernel.org: fixed checkpatch warning] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/errata/mips/errata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/errata/mips/errata.c b/arch/riscv/errata/mips/errata.c index e984a8152208..2c3dc2259e93 100644 --- a/arch/riscv/errata/mips/errata.c +++ b/arch/riscv/errata/mips/errata.c @@ -57,7 +57,7 @@ void mips_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, } tmp = (1U << alt->patch_id); - if (cpu_req_errata && tmp) { + if (cpu_req_errata & tmp) { mutex_lock(&text_mutex); patch_text_nosync(ALT_OLD_PTR(alt), ALT_ALT_PTR(alt), alt->alt_len); From 96b8b9d0dead730ede84e81df473552cd4127c70 Mon Sep 17 00:00:00 2001 From: "Guo Ren (Alibaba DAMO Academy)" Date: Sun, 25 Jan 2026 00:52:12 -0500 Subject: [PATCH 184/697] riscv: mm: Fixup no5lvl failure when vaddr is invalid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit db909bd7986c10da074917af3dae83a60fa65093 ] Unlike no4lvl, no5lvl still continues to detect satp, which requires va=pa mapping. When pa=0x800000000000, no5lvl would fail in Sv48 mode due to an illegal VA value of 0x800000000000. So, prevent detecting the satp flow for no5lvl, when vaddr is invalid. Add the is_vaddr_valid() function for checking. Fixes: 26e7aacb83df ("riscv: Allow to downgrade paging mode from the command line") Cc: Alexandre Ghiti Cc: Björn Töpel Signed-off-by: Guo Ren (Alibaba DAMO Academy) Tested-by: Fangyu Yu Link: https://patch.msgid.link/20260125055212.433163-1-guoren@kernel.org [pjw@kernel.org: cleaned up commit message] Signed-off-by: Paul Walmsley Signed-off-by: Sasha Levin --- arch/riscv/mm/init.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index d85efe74a4b6..ee40ca01ac66 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -852,6 +852,27 @@ static void __init set_mmap_rnd_bits_max(void) mmap_rnd_bits_max = MMAP_VA_BITS - PAGE_SHIFT - 3; } +static bool __init is_vaddr_valid(unsigned long va) +{ + unsigned long up = 0; + + switch (satp_mode) { + case SATP_MODE_39: + up = 1UL << 38; + break; + case SATP_MODE_48: + up = 1UL << 47; + break; + case SATP_MODE_57: + up = 1UL << 56; + break; + default: + return false; + } + + return (va < up) || (va >= (ULONG_MAX - up + 1)); +} + /* * There is a simple way to determine if 4-level is supported by the * underlying hardware: establish 1:1 mapping in 4-level page table mode @@ -893,6 +914,9 @@ static __init void set_satp_mode(uintptr_t dtb_pa) set_satp_mode_pmd + PMD_SIZE, PMD_SIZE, PAGE_KERNEL_EXEC); retry: + if (!is_vaddr_valid(set_satp_mode_pmd)) + goto out; + create_pgd_mapping(early_pg_dir, set_satp_mode_pmd, pgtable_l5_enabled ? @@ -915,6 +939,7 @@ static __init void set_satp_mode(uintptr_t dtb_pa) disable_pgtable_l4(); } +out: memset(early_pg_dir, 0, PAGE_SIZE); memset(early_p4d, 0, PAGE_SIZE); memset(early_pud, 0, PAGE_SIZE); From 91e4446b35f657915db0a6623aeeb0ea4c27a1f0 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 25 Apr 2026 11:41:53 +0800 Subject: [PATCH 185/697] kunit: config: Enable KUNIT_DEBUGFS by default [ Upstream commit 17e4c68ff35090d8cb743e3c82c09f92fda1ebda ] The KUNIT_DEBUGFS option is currently enabled based on the value of KUNIT_ALL_TESTS, but it really doesn't have anything to do with the set of enabled tests, so just enable it by default anyway. In particular, this shouldn't be only visible if KUNIT_ALL_TESTS is set, which is quite confusing. Link: https://lore.kernel.org/r/20260425034155.53913-1-david@davidgow.net Fixes: beaed42c427d ("kunit: default KUNIT_* fragments to KUNIT_ALL_TESTS") Signed-off-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- lib/kunit/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index 7a6af361d2fc..2b9fd107a69f 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -16,8 +16,8 @@ menuconfig KUNIT if KUNIT config KUNIT_DEBUGFS - bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS - default KUNIT_ALL_TESTS + bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" + default y help Enable debugfs representation for kunit. Currently this consists of /sys/kernel/debug/kunit//results files for each From 3f4d827800017ed5244b8cd1b9d0f8217e1417ff Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 25 Apr 2026 11:41:54 +0800 Subject: [PATCH 186/697] kunit: config: KUNIT_DEBUGFS should depend on DEBUG_FS [ Upstream commit 8f80b5b227ef9ea422080487715c841856339aed ] CONFIG_KUNIT_DEBUGFS is totally useless without debugfs, so it should depend on CONFIG_DEBUG_FS. Link: https://lore.kernel.org/r/20260425034155.53913-2-david@davidgow.net Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") Signed-off-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- lib/kunit/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index 2b9fd107a69f..889380c2702c 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -17,6 +17,7 @@ if KUNIT config KUNIT_DEBUGFS bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" + depends on DEBUG_FS default y help Enable debugfs representation for kunit. Currently this consists From fd2b01637e56d1098ab0dc9a2f0f64090e4a6e25 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Tue, 28 Apr 2026 17:44:58 +0530 Subject: [PATCH 187/697] pinctrl: qcom: Fix wakeirq map by removing disconnected irqs for sm8150 [ Upstream commit 52ac35b8a151446481496404af3a8e5e889b3c5a ] PDC interrupts 122-125 were meant for ibi_i3c wakeup but sm8150 do not support i3c. GPIOs 39,51,88 and 144 are also connected to different PDC pin and already reflected in the wake irq map. Remove the unsupported wakeup interrupts from the map. Fixes: 90337380c809 ("pinctrl: qcom: sm8150: Specify PDC map") Reviewed-by: Konrad Dybcio Signed-off-by: Maulik Shah Signed-off-by: Navya Malempati Signed-off-by: Linus Walleij Signed-off-by: Sasha Levin --- drivers/pinctrl/qcom/pinctrl-sm8150.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-sm8150.c b/drivers/pinctrl/qcom/pinctrl-sm8150.c index ad861cd66958..e4c561a9c50a 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8150.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8150.c @@ -1496,18 +1496,18 @@ static const struct msm_gpio_wakeirq_map sm8150_pdc_map[] = { { 3, 31 }, { 5, 32 }, { 8, 33 }, { 9, 34 }, { 10, 100 }, { 12, 104 }, { 24, 37 }, { 26, 38 }, { 27, 41 }, { 28, 42 }, { 30, 39 }, { 36, 43 }, { 37, 44 }, { 38, 30 }, { 39, 118 }, - { 39, 125 }, { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, - { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 51, 123 }, + { 41, 47 }, { 42, 48 }, { 46, 50 }, { 47, 49 }, + { 48, 51 }, { 49, 53 }, { 50, 52 }, { 51, 116 }, { 53, 54 }, { 54, 55 }, { 55, 56 }, { 56, 57 }, { 58, 58 }, { 60, 60 }, { 61, 61 }, { 68, 62 }, { 70, 63 }, { 76, 71 }, { 77, 66 }, { 81, 64 }, { 83, 65 }, { 86, 67 }, { 87, 84 }, - { 88, 117 }, { 88, 124 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, + { 88, 117 }, { 90, 69 }, { 91, 70 }, { 93, 75 }, { 95, 72 }, { 96, 73 }, { 97, 74 }, { 101, 40 }, { 103, 77 }, { 104, 78 }, { 108, 79 }, { 112, 80 }, { 113, 81 }, { 114, 82 }, { 117, 85 }, { 118, 101 }, { 119, 87 }, { 120, 88 }, { 121, 89 }, { 122, 90 }, { 123, 91 }, { 124, 92 }, { 125, 93 }, { 129, 94 }, { 132, 105 }, { 133, 83 }, { 134, 36 }, { 136, 97 }, { 142, 103 }, - { 144, 115 }, { 144, 122 }, { 147, 102 }, { 150, 107 }, + { 144, 115 }, { 147, 102 }, { 150, 107 }, { 152, 108 }, { 153, 109 } }; From f39bc7ebe75e2186b417a024a7f7e2fd4cc7eb95 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:30 +0100 Subject: [PATCH 188/697] firmware: arm_ffa: Bound PARTITION_INFO_GET_REGS copies [ Upstream commit 3974ea1938406f9bfa7c1f48d4e43533f447bb08 ] The register-based PARTITION_INFO_GET path trusted the firmware-provided indices when copying partition descriptors into the caller buffer. Reject inconsistent counts or index progressions so the copy loop cannot write past the allocated array. Fixes: ba85c644ac8d ("firmware: arm_ffa: Add support for FFA_PARTITION_INFO_GET_REGS") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-6-8595ae450034@kernel.org (fixed cur_idx when exactly one descriptor in the first fragment) Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 287db8528610..e597a9cf64dc 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -319,6 +319,12 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, #define PART_INFO_ID_MASK GENMASK(15, 0) #define PART_INFO_EXEC_CXT_MASK GENMASK(31, 16) #define PART_INFO_PROPS_MASK GENMASK(63, 32) +#define FFA_PART_INFO_GET_REGS_FIRST_REG 3 +#define FFA_PART_INFO_GET_REGS_REGS_PER_DESC 3 +#define FFA_PART_INFO_GET_REGS_MAX_DESC \ + (((sizeof(ffa_value_t) / sizeof_field(ffa_value_t, a0)) - \ + FFA_PART_INFO_GET_REGS_FIRST_REG) / \ + FFA_PART_INFO_GET_REGS_REGS_PER_DESC) #define PART_INFO_ID(x) ((u16)(FIELD_GET(PART_INFO_ID_MASK, (x)))) #define PART_INFO_EXEC_CXT(x) ((u16)(FIELD_GET(PART_INFO_EXEC_CXT_MASK, (x)))) #define PART_INFO_PROPERTIES(x) ((u32)(FIELD_GET(PART_INFO_PROPS_MASK, (x)))) @@ -326,15 +332,13 @@ static int __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, struct ffa_partition_info *buffer, int num_parts) { - u16 buf_sz, start_idx, cur_idx, count = 0, prev_idx = 0, tag = 0; + u16 buf_sz, start_idx = 0, cur_idx, count = 0, tag = 0; struct ffa_partition_info *buf = buffer; ffa_value_t partition_info; do { __le64 *regs; - int idx; - - start_idx = prev_idx ? prev_idx + 1 : 0; + int idx, nr_desc, buf_idx; invoke_ffa_fn((ffa_value_t){ .a0 = FFA_PARTITION_INFO_GET_REGS, @@ -350,15 +354,28 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, count = PARTITION_COUNT(partition_info.a2); if (!buffer || !num_parts) /* count only */ return count; + if (count > num_parts) + return -EINVAL; cur_idx = CURRENT_INDEX(partition_info.a2); + if (cur_idx < start_idx || cur_idx >= count) + return -EINVAL; + + nr_desc = cur_idx - start_idx + 1; + if (nr_desc > FFA_PART_INFO_GET_REGS_MAX_DESC) + return -EINVAL; + + buf_idx = buf - buffer; + if (buf_idx + nr_desc > num_parts) + return -EINVAL; + tag = UUID_INFO_TAG(partition_info.a2); buf_sz = PARTITION_INFO_SZ(partition_info.a2); if (buf_sz > sizeof(*buffer)) buf_sz = sizeof(*buffer); regs = (void *)&partition_info.a3; - for (idx = 0; idx < cur_idx - start_idx + 1; idx++, buf++) { + for (idx = 0; idx < nr_desc; idx++, buf++) { union { uuid_t uuid; u64 regs[2]; @@ -376,7 +393,7 @@ __ffa_partition_info_get_regs(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, uuid_copy(&buf->uuid, &uuid_regs.uuid); regs += 3; } - prev_idx = cur_idx; + start_idx = cur_idx + 1; } while (cur_idx < (count - 1)); From 0a5dbac5ef53a8f953164e1da471b698de3d1bb0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:31 +0100 Subject: [PATCH 189/697] firmware: arm_ffa: Keep framework RX release under lock [ Upstream commit 2af18f8e36b277730527cacc2256b1332f56aa28 ] The framework notification handler drops rx_lock before issuing FFA_RX_RELEASE, leaving a window where another RX-buffer user can start a new FF-A transaction before ownership has actually been returned to firmware. Move the FFA_RX_RELEASE calls so they execute while rx_lock is still held on both the kmemdup() failure path and the normal success path. While doing that, switch the handler to scoped_guard() to keep the critical section explicit. Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-7-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index e597a9cf64dc..c6a9bf3497cf 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1488,25 +1488,22 @@ static void handle_fwk_notif_callbacks(u32 bitmap) if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) return; - mutex_lock(&drv_info->rx_lock); + scoped_guard(mutex, &drv_info->rx_lock) { + msg = drv_info->rx_buffer; + buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); + if (!buf) { + ffa_rx_release(); + return; + } - msg = drv_info->rx_buffer; - buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); - if (!buf) { - mutex_unlock(&drv_info->rx_lock); - return; + target = SENDER_ID(msg->send_recv_id); + if (msg->offset >= sizeof(*msg)) + uuid_copy(&uuid, &msg->uuid); + else + uuid_copy(&uuid, &uuid_null); + ffa_rx_release(); } - target = SENDER_ID(msg->send_recv_id); - if (msg->offset >= sizeof(*msg)) - uuid_copy(&uuid, &msg->uuid); - else - uuid_copy(&uuid, &uuid_null); - - mutex_unlock(&drv_info->rx_lock); - - ffa_rx_release(); - read_lock(&drv_info->notify_lock); cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); read_unlock(&drv_info->notify_lock); From 3c51d99449dc5a01c08a7fce6071d6721f5aac83 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:32 +0100 Subject: [PATCH 190/697] firmware: arm_ffa: Validate framework notification message layout [ Upstream commit 4a1cc9e96b311d2609a6f963a5e35bd4ae730d97 ] Framework notifications carry an indirect message in the shared RX buffer. Validate the reported offset and size before using them, reject zero-length payloads, and ensure that any non-header payload starts at the UUID field rather than in the middle of the message header. Use the validated offset and size values for both kmemdup() and the UUID parsing path so malformed firmware data cannot drive an out-of-bounds read or an oversized allocation. Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-8-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index c6a9bf3497cf..f96bb84af55e 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1483,21 +1483,35 @@ static void handle_fwk_notif_callbacks(u32 bitmap) int notify_id = 0, target; struct ffa_indirect_msg_hdr *msg; struct notifier_cb_info *cb_info = NULL; + size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); /* Only one framework notification defined and supported for now */ if (!(bitmap & FRAMEWORK_NOTIFY_RX_BUFFER_FULL)) return; scoped_guard(mutex, &drv_info->rx_lock) { + u32 offset, size; + msg = drv_info->rx_buffer; - buf = kmemdup((void *)msg + msg->offset, msg->size, GFP_KERNEL); + offset = msg->offset; + size = msg->size; + + if (!size || (offset != min_offset && offset < sizeof(*msg)) || + offset > drv_info->rxtx_bufsz || + size > drv_info->rxtx_bufsz - offset) { + pr_err("invalid framework notification message\n"); + ffa_rx_release(); + return; + } + + buf = kmemdup((void *)msg + offset, size, GFP_KERNEL); if (!buf) { ffa_rx_release(); return; } target = SENDER_ID(msg->send_recv_id); - if (msg->offset >= sizeof(*msg)) + if (offset >= sizeof(*msg)) uuid_copy(&uuid, &msg->uuid); else uuid_copy(&uuid, &uuid_null); From 419cef661ae86a591c6c5d8534b2909706399824 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:33 +0100 Subject: [PATCH 191/697] firmware: arm_ffa: Align RxTx buffer size before mapping [ Upstream commit 0399e3f872ca3d78044bb715a73ea645806d2c7b ] Commit 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") advertises PAGE_ALIGN(rxtx_bufsz) to firmware when mapping the buffers but the driver continues to stores the minimum FF-A buffer size in drv_info->rxtx_bufsz which is used elsewhere in the driver. Align the size before storing it so that the allocation, validation and FFA_RXTX_MAP all use the same buffer size. Fixes: 83210251fd70 ("firmware: arm_ffa: Use the correct buffer size during RXTX_MAP") Cc: Sebastian Ene Link: https://sashiko.dev/#/patchset/20260402113939.930221-1-sebastianene@google.com Reviewed-by: Sebastian Ene Link: https://patch.msgid.link/20260428-ffa_fixes-v2-9-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index f96bb84af55e..70f34f58f383 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -2102,6 +2102,7 @@ static int __init ffa_init(void) rxtx_bufsz = SZ_4K; } + rxtx_bufsz = PAGE_ALIGN(rxtx_bufsz); drv_info->rxtx_bufsz = rxtx_bufsz; drv_info->rx_buffer = alloc_pages_exact(rxtx_bufsz, GFP_KERNEL); if (!drv_info->rx_buffer) { @@ -2117,7 +2118,7 @@ static int __init ffa_init(void) ret = ffa_rxtx_map(virt_to_phys(drv_info->tx_buffer), virt_to_phys(drv_info->rx_buffer), - PAGE_ALIGN(rxtx_bufsz) / FFA_PAGE_SIZE); + rxtx_bufsz / FFA_PAGE_SIZE); if (ret) { pr_err("failed to register FFA RxTx buffers\n"); goto free_pages; From d1e38551fadea230649bc428f0f35c9ee062a072 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:34 +0100 Subject: [PATCH 192/697] firmware: arm_ffa: Snapshot notifier callbacks under lock [ Upstream commit 38290b180a4d5746baed796d49f88d56d2f336cd ] Both notification handlers currently look up a notifier callback under notify_lock, drop the lock, and then dereference the returned notifier entry. A concurrent unregister can delete and free that entry in the gap, leaving the handler to dereference stale memory. Copy the callback pointer and callback data while notify_lock is still held and invoke the callback only after the lock is dropped. This keeps the existing callback execution model while removing the use-after-free window in both the framework and non-framework notification paths. Fixes: 285a5ea0f542 ("firmware: arm_ffa: Add support for handling framework notifications") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-10-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 35 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index 70f34f58f383..f518c8703102 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1459,20 +1459,25 @@ static int ffa_notify_send(struct ffa_device *dev, int notify_id, static void handle_notif_callbacks(u64 bitmap, enum notify_type type) { + ffa_notifier_cb cb; + void *cb_data; int notify_id; - struct notifier_cb_info *cb_info = NULL; for (notify_id = 0; notify_id <= FFA_MAX_NOTIFICATIONS && bitmap; notify_id++, bitmap >>= 1) { if (!(bitmap & 1)) continue; - read_lock(&drv_info->notify_lock); - cb_info = notifier_hnode_get_by_type(notify_id, type); - read_unlock(&drv_info->notify_lock); + scoped_guard(read_lock, &drv_info->notify_lock) { + struct notifier_cb_info *cb_info; + + cb_info = notifier_hnode_get_by_type(notify_id, type); + cb = cb_info ? cb_info->cb : NULL; + cb_data = cb_info ? cb_info->cb_data : NULL; + } - if (cb_info && cb_info->cb) - cb_info->cb(notify_id, cb_info->cb_data); + if (cb) + cb(notify_id, cb_data); } } @@ -1480,9 +1485,10 @@ static void handle_fwk_notif_callbacks(u32 bitmap) { void *buf; uuid_t uuid; + void *fwk_cb_data; int notify_id = 0, target; + ffa_fwk_notifier_cb fwk_cb; struct ffa_indirect_msg_hdr *msg; - struct notifier_cb_info *cb_info = NULL; size_t min_offset = offsetof(struct ffa_indirect_msg_hdr, uuid); /* Only one framework notification defined and supported for now */ @@ -1518,12 +1524,17 @@ static void handle_fwk_notif_callbacks(u32 bitmap) ffa_rx_release(); } - read_lock(&drv_info->notify_lock); - cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, &uuid); - read_unlock(&drv_info->notify_lock); + scoped_guard(read_lock, &drv_info->notify_lock) { + struct notifier_cb_info *cb_info; + + cb_info = notifier_hnode_get_by_vmid_uuid(notify_id, target, + &uuid); + fwk_cb = cb_info ? cb_info->fwk_cb : NULL; + fwk_cb_data = cb_info ? cb_info->cb_data : NULL; + } - if (cb_info && cb_info->fwk_cb) - cb_info->fwk_cb(notify_id, cb_info->cb_data, buf); + if (fwk_cb) + fwk_cb(notify_id, fwk_cb_data, buf); kfree(buf); } From 9e472874c9541a34ad2f3377177b28d9adb8e2f5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 28 Apr 2026 19:33:35 +0100 Subject: [PATCH 193/697] firmware: arm_ffa: Fix sched-recv callback partition lookup [ Upstream commit a6848a50404eefb6f0b131c21881a2d8d21b31a9 ] ffa_sched_recv_cb_update() used list_for_each_entry_safe() to search for a matching partition and then tested the iterator against NULL. That is not a valid end-of-list check for circular lists and can fall through with an invalid pointer. Use a normal iterator and detect the not-found case correctly before touching the partition state. Fixes: be61da938576 ("firmware: arm_ffa: Allow multiple UUIDs per partition to register SRI callback") Link: https://patch.msgid.link/20260428-ffa_fixes-v2-11-8595ae450034@kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Sasha Levin --- drivers/firmware/arm_ffa/driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c index f518c8703102..827aac08a8e9 100644 --- a/drivers/firmware/arm_ffa/driver.c +++ b/drivers/firmware/arm_ffa/driver.c @@ -1203,7 +1203,7 @@ static int ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, void *cb_data, bool is_registration) { - struct ffa_dev_part_info *partition = NULL, *tmp; + struct ffa_dev_part_info *partition = NULL; struct list_head *phead; bool cb_valid; @@ -1216,11 +1216,11 @@ ffa_sched_recv_cb_update(struct ffa_device *dev, ffa_sched_recv_cb callback, return -EINVAL; } - list_for_each_entry_safe(partition, tmp, phead, node) + list_for_each_entry(partition, phead, node) if (partition->dev == dev) break; - if (!partition) { + if (&partition->node == phead) { pr_err("%s: No such partition ID 0x%x\n", __func__, dev->vm_id); return -EINVAL; } From e984dc22e2c24dc34d6728e338c82b1ce7862753 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 5 May 2026 21:15:37 +0200 Subject: [PATCH 194/697] ARM: integrator: Fix early initialization [ Upstream commit 90d77b30a666049ad24df463f52e5d529c44e8cd ] Starting with commit bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap"), intcp_init_early calls syscon_regmap_lookup_by_compatible which in turn calls of_syscon_register. This function allocates memory. Since the memory management code has not been initialized at that time, the call always fails. It either returns -ENOMEM or crashes as follows. Unable to handle kernel NULL pointer dereference at virtual address 0000000c when read [0000000c] *pgd=00000000 Internal error: Oops: 5 [#1] ARM Modules linked in: CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.15.0-rc5-00026-g5fcc9bf84ee5 #1 PREEMPT Hardware name: ARM Integrator/CP (Device Tree) PC is at __kmalloc_cache_noprof+0xec/0x39c LR is at __kmalloc_cache_noprof+0x34/0x39c ... Call trace: __kmalloc_cache_noprof from of_syscon_register+0x7c/0x310 of_syscon_register from device_node_get_regmap+0xa4/0xb0 device_node_get_regmap from intcp_init_early+0xc/0x40 intcp_init_early from start_kernel+0x60/0x688 start_kernel from 0x0 The crash is seen due to a dereferenced pointer which is not supposed to be NULL but is NULL if the memory management subsystem has not been initialized. The crash is not seen with all versions of gcc. Some versions such as gcc 9.x apparently do not dereference the pointer, presumably if tracing is disabled. The problem has been reproduced with gcc 10.x, 11.x, and 13.x. Either case, if the crash is not seen, the call to syscon_regmap_lookup_by_compatible returns -ENOMEM, and sched_clock_register is never called. Fix the problem by moving the early initialization code into the standard machine initialization code. Fixes: bdb249fce9ad4 ("ARM: integrator: read counter using syscon/regmap") Cc: Linus Walleij Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/20250518164118.3859567-1-linux@roeck-us.net Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20260505-integrator-fixes-v1-1-56ab9aac59db@kernel.org Signed-off-by: Arnd Bergmann Signed-off-by: Sasha Levin --- arch/arm/mach-versatile/integrator_cp.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-versatile/integrator_cp.c b/arch/arm/mach-versatile/integrator_cp.c index 2ed4ded56b3f..03dfb5f720b7 100644 --- a/arch/arm/mach-versatile/integrator_cp.c +++ b/arch/arm/mach-versatile/integrator_cp.c @@ -86,14 +86,6 @@ static u64 notrace intcp_read_sched_clock(void) return val; } -static void __init intcp_init_early(void) -{ - cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); - if (IS_ERR(cm_map)) - return; - sched_clock_register(intcp_read_sched_clock, 32, 24000000); -} - static void __init intcp_init_irq_of(void) { cm_init(); @@ -119,6 +111,10 @@ static void __init intcp_init_of(void) { struct device_node *cpcon; + cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator"); + if (!IS_ERR(cm_map)) + sched_clock_register(intcp_read_sched_clock, 32, 24000000); + cpcon = of_find_matching_node(NULL, intcp_syscon_match); if (!cpcon) return; @@ -138,7 +134,6 @@ static const char * intcp_dt_board_compat[] = { DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)") .reserve = integrator_reserve, .map_io = intcp_map_io, - .init_early = intcp_init_early, .init_irq = intcp_init_irq_of, .init_machine = intcp_init_of, .dt_compat = intcp_dt_board_compat, From fecae8b1fb2d3790e19a134fead83cee51189644 Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Tue, 28 Apr 2026 16:01:39 +0800 Subject: [PATCH 195/697] ALSA: hda: cs35l56: Put ACPI device after setting companion [ Upstream commit aa2fbece1b07954ef26488c800d126a36a8ab93e ] acpi_dev_get_first_match_dev() returns a refcounted ACPI device and callers are expected to balance it with acpi_dev_put(). When no companion is already attached, cs35l56_hda_read_acpi() looks up an ACPI device and sets it with ACPI_COMPANION_SET(), but leaves the lookup reference held. ACPI_COMPANION_SET() does not take ownership of that reference, so drop it with acpi_dev_put() after attaching the companion. Fixes: 73cfbfa9caea ("ALSA: hda/cs35l56: Add driver for Cirrus Logic CS35L56 amplifier") Signed-off-by: Shuhao Fu Tested-by: Simon Trimmer Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260428080139.GA1649104@chcpu16 Signed-off-by: Sasha Levin --- sound/hda/codecs/side-codecs/cs35l56_hda.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/side-codecs/cs35l56_hda.c b/sound/hda/codecs/side-codecs/cs35l56_hda.c index 79c15e21d4bc..1d25fe01066e 100644 --- a/sound/hda/codecs/side-codecs/cs35l56_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l56_hda.c @@ -949,6 +949,7 @@ static int cs35l56_hda_read_acpi(struct cs35l56_hda *cs35l56, int hid, int id) return -ENODEV; } ACPI_COMPANION_SET(cs35l56->base.dev, adev); + acpi_dev_put(adev); } /* Initialize things that could be overwritten by a fixup */ From 373f65b448ed4d7ced253724aa36babb808c72ff Mon Sep 17 00:00:00 2001 From: Shuhao Fu Date: Tue, 28 Apr 2026 16:12:38 +0800 Subject: [PATCH 196/697] ALSA: hda: cs35l41: Put ACPI device on missing physical node [ Upstream commit fca7401fe37f7abc6e54147ea560f37279231137 ] acpi_dev_get_first_match_dev() returns a refcounted ACPI device and callers must balance it with acpi_dev_put(). cs35l41_hda_read_acpi() stores the returned ACPI device in cs35l41->dacpi. That reference is normally released by the later probe cleanup or the remove path, but the NULL-check on physdev exits before either of those paths can run. Drop the lookup reference before returning -ENODEV. Fixes: c34b04cc6178 ("ALSA: hda: cs35l41: Fix NULL pointer dereference in cs35l41_hda_read_acpi()") Signed-off-by: Shuhao Fu Tested-by: Simon Trimmer Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260428081238.GA1659932@chcpu16 Signed-off-by: Sasha Levin --- sound/hda/codecs/side-codecs/cs35l41_hda.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c index 21e00055c0c4..47263c5a021c 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c @@ -1901,8 +1901,10 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i cs35l41->dacpi = adev; physdev = get_device(acpi_get_first_physical_node(adev)); - if (!physdev) + if (!physdev) { + acpi_dev_put(adev); return -ENODEV; + } sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); if (IS_ERR(sub)) From c32a7e0e3c73c1c0768556a56bd78de9f7b83780 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 28 Apr 2026 16:58:56 +0100 Subject: [PATCH 197/697] btrfs: tracepoints: fix sleep while in atomic context in btrfs_sync_file() [ Upstream commit c73370c677646e86fc4b1780fb07027bdf847375 ] The trace event btrfs_sync_file() is called in an atomic context (all trace events are) and its call to dput(), which is needed due to the call to dget_parent(), can sleep, triggering a kernel splat. This can be reproduced by enabling the trace event and running btrfs/056 from fstests for example. The splat shown in dmesg is the following: [53.919] BUG: sleeping function called from invalid context at fs/dcache.c:970 [53.947] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 32773, name: xfs_io [53.988] preempt_count: 2, expected: 0 [53.967] RCU nest depth: 0, expected: 0 [53.943] Preemption disabled at: [53.944] [<0000000000000000>] 0x0 [54.078] CPU: 0 UID: 0 PID: 32773 Comm: xfs_io Tainted: G W 7.1.0-rc1-btrfs-next-232+ #1 PREEMPT(full) [54.070] Tainted: [W]=WARN [54.071] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 [54.072] Call Trace: [54.074] [54.076] dump_stack_lvl+0x56/0x80 [54.079] __might_resched.cold+0xd6/0x10f [54.072] dput.part.0+0x24/0x110 [54.078] trace_event_raw_event_btrfs_sync_file+0x75/0x140 [btrfs] [54.089] btrfs_sync_file+0x1ed/0x530 [btrfs] [54.087] ? __handle_mm_fault+0x8ae/0xed0 [54.089] btrfs_do_write_iter+0x172/0x210 [btrfs] [54.091] vfs_write+0x21f/0x450 [54.094] __x64_sys_pwrite64+0x8d/0xc0 [54.096] ? do_user_addr_fault+0x20c/0x670 [54.099] do_syscall_64+0x60/0xf20 [54.092] ? clear_bhb_loop+0x60/0xb0 [54.094] entry_SYSCALL_64_after_hwframe+0x76/0x7e So stop using dget_parent() and dput() and access the parent dentry directly as dentry->d_parent. This is also what ext4 is doing in its equivalent trace event ext4_sync_file_enter(). Fixes: a85b46db143f ("btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()") Reviewed-by: Boris Burkov Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- include/trace/events/btrfs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 0864700f76e0..fa090a455037 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -771,10 +771,8 @@ TRACE_EVENT(btrfs_sync_file, TP_fast_assign( struct dentry *dentry = file_dentry(file); struct inode *inode = file_inode(file); - struct dentry *parent = dget_parent(dentry); - struct inode *parent_inode = d_inode(parent); + struct inode *parent_inode = d_inode(dentry->d_parent); - dput(parent); TP_fast_assign_fsid(btrfs_sb(inode->i_sb)); __entry->ino = btrfs_ino(BTRFS_I(inode)); __entry->parent = btrfs_ino(BTRFS_I(parent_inode)); From a9b2f73f6ba7c3f92ae483686b9a92797a290071 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:16 +0200 Subject: [PATCH 198/697] netfilter: x_tables: unregister the templates first [ Upstream commit d338693d778579b676a61346849bebd892427158 ] When the module is going away we need to zap the template first. Else there is a small race window where userspace could instantiate a new table after the pernet exit function has removed the current table. Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") Reported-by: Tristan Madani Reviewed-by: Tristan Madani Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv4/netfilter/arptable_filter.c | 2 +- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 2 +- net/ipv4/netfilter/iptable_raw.c | 2 +- net/ipv4/netfilter/iptable_security.c | 2 +- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 2 +- net/ipv6/netfilter/ip6table_raw.c | 2 +- net/ipv6/netfilter/ip6table_security.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 78cd5ee24448..359d00d74095 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -82,8 +82,8 @@ static int __init arptable_filter_init(void) static void __exit arptable_filter_fini(void) { - unregister_pernet_subsys(&arptable_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&arptable_filter_net_ops); kfree(arpfilter_ops); } diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 3ab908b74795..595bfb492b1c 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -101,8 +101,8 @@ static int __init iptable_filter_init(void) static void __exit iptable_filter_fini(void) { - unregister_pernet_subsys(&iptable_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&iptable_filter_net_ops); kfree(filter_ops); } diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 385d945d8ebe..db90db7057cc 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -135,8 +135,8 @@ static int __init iptable_mangle_init(void) static void __exit iptable_mangle_fini(void) { - unregister_pernet_subsys(&iptable_mangle_net_ops); xt_unregister_template(&packet_mangler); + unregister_pernet_subsys(&iptable_mangle_net_ops); kfree(mangle_ops); } diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 0e7f53964d0a..b46a79091730 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -100,9 +100,9 @@ static int __init iptable_raw_init(void) static void __exit iptable_raw_fini(void) { + xt_unregister_template(&packet_raw); unregister_pernet_subsys(&iptable_raw_net_ops); kfree(rawtable_ops); - xt_unregister_template(&packet_raw); } module_init(iptable_raw_init); diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index d885443cb267..2b89adc1e575 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -89,9 +89,9 @@ static int __init iptable_security_init(void) static void __exit iptable_security_fini(void) { + xt_unregister_template(&security_table); unregister_pernet_subsys(&iptable_security_net_ops); kfree(sectbl_ops); - xt_unregister_template(&security_table); } module_init(iptable_security_init); diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index e8992693e14a..9dcd4501fe80 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -100,8 +100,8 @@ static int __init ip6table_filter_init(void) static void __exit ip6table_filter_fini(void) { - unregister_pernet_subsys(&ip6table_filter_net_ops); xt_unregister_template(&packet_filter); + unregister_pernet_subsys(&ip6table_filter_net_ops); kfree(filter_ops); } diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 8dd4cd0c47bd..ce2cbce9e3ed 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -128,8 +128,8 @@ static int __init ip6table_mangle_init(void) static void __exit ip6table_mangle_fini(void) { - unregister_pernet_subsys(&ip6table_mangle_net_ops); xt_unregister_template(&packet_mangler); + unregister_pernet_subsys(&ip6table_mangle_net_ops); kfree(mangle_ops); } diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index fc9f6754028f..8af0f8bd036d 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -98,8 +98,8 @@ static int __init ip6table_raw_init(void) static void __exit ip6table_raw_fini(void) { - unregister_pernet_subsys(&ip6table_raw_net_ops); xt_unregister_template(&packet_raw); + unregister_pernet_subsys(&ip6table_raw_net_ops); kfree(rawtable_ops); } diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 4df14a9bae78..66018b169b01 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -88,8 +88,8 @@ static int __init ip6table_security_init(void) static void __exit ip6table_security_fini(void) { - unregister_pernet_subsys(&ip6table_security_net_ops); xt_unregister_template(&security_table); + unregister_pernet_subsys(&ip6table_security_net_ops); kfree(sectbl_ops); } From 89ebafe7910d51c745185f55ed663f38b8e43d89 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:15 +0200 Subject: [PATCH 199/697] netfilter: x_tables: add and use xt_unregister_table_pre_exit [ Upstream commit 527d6931473b75d90e38942aae6537d1a527f1fd ] Remove the copypasted variants of _pre_exit and add one single function in the xtables core. ebtables is not compatible with x_tables and therefore unchanged. This is a preparation patch to reduce noise in the followup bug fixes. Reviewed-by: Tristan Madani Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Stable-dep-of: b4597d5fd7d2 ("netfilter: x_tables: add and use xtables_unregister_table_exit") Signed-off-by: Sasha Levin --- include/linux/netfilter/x_tables.h | 1 + include/linux/netfilter_arp/arp_tables.h | 1 - include/linux/netfilter_ipv4/ip_tables.h | 1 - include/linux/netfilter_ipv6/ip6_tables.h | 1 - net/ipv4/netfilter/arp_tables.c | 9 ------- net/ipv4/netfilter/arptable_filter.c | 2 +- net/ipv4/netfilter/ip_tables.c | 9 ------- net/ipv4/netfilter/iptable_filter.c | 2 +- net/ipv4/netfilter/iptable_mangle.c | 2 +- net/ipv4/netfilter/iptable_nat.c | 1 + net/ipv4/netfilter/iptable_raw.c | 2 +- net/ipv4/netfilter/iptable_security.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 9 ------- net/ipv6/netfilter/ip6table_filter.c | 2 +- net/ipv6/netfilter/ip6table_mangle.c | 2 +- net/ipv6/netfilter/ip6table_nat.c | 1 + net/ipv6/netfilter/ip6table_raw.c | 2 +- net/ipv6/netfilter/ip6table_security.c | 2 +- net/netfilter/x_tables.c | 29 +++++++++++++++++++++++ 19 files changed, 41 insertions(+), 39 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 77c778d84d4c..f7916fd0e807 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -300,6 +300,7 @@ struct xt_table *xt_register_table(struct net *net, struct xt_table_info *bootstrap, struct xt_table_info *newinfo); void *xt_unregister_table(struct xt_table *table); +void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters, diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index a40aaf645fa4..05631a25e622 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -53,7 +53,6 @@ int arpt_register_table(struct net *net, const struct xt_table *table, const struct arpt_replace *repl, const struct nf_hook_ops *ops); void arpt_unregister_table(struct net *net, const char *name); -void arpt_unregister_table_pre_exit(struct net *net, const char *name); extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index 132b0e4a6d4d..13593391d605 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -26,7 +26,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, const struct ipt_replace *repl, const struct nf_hook_ops *ops); -void ipt_unregister_table_pre_exit(struct net *net, const char *name); void ipt_unregister_table_exit(struct net *net, const char *name); /* Standard entry. */ diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 8b8885a73c76..c6d5b927830d 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -27,7 +27,6 @@ extern void *ip6t_alloc_initial_table(const struct xt_table *); int ip6t_register_table(struct net *net, const struct xt_table *table, const struct ip6t_replace *repl, const struct nf_hook_ops *ops); -void ip6t_unregister_table_pre_exit(struct net *net, const char *name); void ip6t_unregister_table_exit(struct net *net, const char *name); extern unsigned int ip6t_do_table(void *priv, struct sk_buff *skb, const struct nf_hook_state *state); diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 97ead883e4a1..d19fce858980 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1581,15 +1581,6 @@ int arpt_register_table(struct net *net, return ret; } -void arpt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); -} -EXPORT_SYMBOL(arpt_unregister_table_pre_exit); - void arpt_unregister_table(struct net *net, const char *name) { struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 359d00d74095..382345567a60 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -43,7 +43,7 @@ static int arptable_filter_table_init(struct net *net) static void __net_exit arptable_filter_net_pre_exit(struct net *net) { - arpt_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_ARP, "filter"); } static void __net_exit arptable_filter_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 23c8deff8095..663868cc5f6d 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1789,14 +1789,6 @@ int ipt_register_table(struct net *net, const struct xt_table *table, return ret; } -void ipt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); -} - void ipt_unregister_table_exit(struct net *net, const char *name) { struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); @@ -1887,7 +1879,6 @@ static void __exit ip_tables_fini(void) } EXPORT_SYMBOL(ipt_register_table); -EXPORT_SYMBOL(ipt_unregister_table_pre_exit); EXPORT_SYMBOL(ipt_unregister_table_exit); EXPORT_SYMBOL(ipt_do_table); module_init(ip_tables_init); diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 595bfb492b1c..0dea754a9120 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -61,7 +61,7 @@ static int __net_init iptable_filter_net_init(struct net *net) static void __net_exit iptable_filter_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "filter"); } static void __net_exit iptable_filter_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index db90db7057cc..4d3b12492308 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -96,7 +96,7 @@ static int iptable_mangle_table_init(struct net *net) static void __net_exit iptable_mangle_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "mangle"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "mangle"); } static void __net_exit iptable_mangle_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 625a1ca13b1b..8fc4912e790d 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -129,6 +129,7 @@ static int iptable_nat_table_init(struct net *net) static void __net_exit iptable_nat_net_pre_exit(struct net *net) { ipt_nat_unregister_lookups(net); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); } static void __net_exit iptable_nat_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index b46a79091730..6f7afec7954b 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -53,7 +53,7 @@ static int iptable_raw_table_init(struct net *net) static void __net_exit iptable_raw_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "raw"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "raw"); } static void __net_exit iptable_raw_net_exit(struct net *net) diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index 2b89adc1e575..81175c20ccbe 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -50,7 +50,7 @@ static int iptable_security_table_init(struct net *net) static void __net_exit iptable_security_net_pre_exit(struct net *net) { - ipt_unregister_table_pre_exit(net, "security"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "security"); } static void __net_exit iptable_security_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index d585ac3c1113..08abf0df0450 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1795,14 +1795,6 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, return ret; } -void ip6t_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); -} - void ip6t_unregister_table_exit(struct net *net, const char *name) { struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); @@ -1894,7 +1886,6 @@ static void __exit ip6_tables_fini(void) } EXPORT_SYMBOL(ip6t_register_table); -EXPORT_SYMBOL(ip6t_unregister_table_pre_exit); EXPORT_SYMBOL(ip6t_unregister_table_exit); EXPORT_SYMBOL(ip6t_do_table); diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 9dcd4501fe80..cf561919bde8 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -60,7 +60,7 @@ static int __net_init ip6table_filter_net_init(struct net *net) static void __net_exit ip6table_filter_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "filter"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "filter"); } static void __net_exit ip6table_filter_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index ce2cbce9e3ed..1a758f2bc537 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -89,7 +89,7 @@ static int ip6table_mangle_table_init(struct net *net) static void __net_exit ip6table_mangle_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "mangle"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "mangle"); } static void __net_exit ip6table_mangle_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 5be723232df8..bb8aa3fc42b4 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -131,6 +131,7 @@ static int ip6table_nat_table_init(struct net *net) static void __net_exit ip6table_nat_net_pre_exit(struct net *net) { ip6t_nat_unregister_lookups(net); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); } static void __net_exit ip6table_nat_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 8af0f8bd036d..923455921c1d 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -52,7 +52,7 @@ static int ip6table_raw_table_init(struct net *net) static void __net_exit ip6table_raw_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "raw"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "raw"); } static void __net_exit ip6table_raw_net_exit(struct net *net) diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index 66018b169b01..c44834d93fc7 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -49,7 +49,7 @@ static int ip6table_security_table_init(struct net *net) static void __net_exit ip6table_security_net_pre_exit(struct net *net) { - ip6t_unregister_table_pre_exit(net, "security"); + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "security"); } static void __net_exit ip6table_security_net_exit(struct net *net) diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 1ca4fa9d249b..2d93f189a79b 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1538,6 +1538,35 @@ void *xt_unregister_table(struct xt_table *table) return private; } EXPORT_SYMBOL_GPL(xt_unregister_table); + +/** + * xt_unregister_table_pre_exit - pre-shutdown unregister of a table + * @net: network namespace + * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) + * @name: name of the table to unregister + * + * Unregisters the specified netfilter table from the given network namespace + * and also unregisters the hooks from netfilter core: no new packets will be + * processed. + */ +void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) +{ + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + struct xt_table *t; + + mutex_lock(&xt[af].mutex); + list_for_each_entry(t, &xt_net->tables[af], list) { + if (strcmp(t->name, name) == 0) { + mutex_unlock(&xt[af].mutex); + + if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ + nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); + return; + } + } + mutex_unlock(&xt[af].mutex); +} +EXPORT_SYMBOL(xt_unregister_table_pre_exit); #endif #ifdef CONFIG_PROC_FS From 86ee5bc9c0f0e652e19f395675a432de11b75514 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:17 +0200 Subject: [PATCH 200/697] netfilter: x_tables: add and use xtables_unregister_table_exit [ Upstream commit b4597d5fd7d2f8cebfffd40dffb5e003cc78964c ] Previous change added xtables_unregister_table_pre_exit to detach the table from the packetpath and to unlink it from the active table list. In case of rmmod, userspace that is doing set/getsockopt for this table will not be able to re-instantiate the table: 1. The larval table has been removed already 2. existing instantiated table is no longer on the xt pernet table list. This adds the second stage helper: unlink the table from the dying list, free the hook ops (if any) and do the audit notification. It replaces xt_unregister_table(). Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") Reported-by: Tristan Madani Reviewed-by: Tristan Madani Closes: https://lore.kernel.org/netfilter-devel/20260429175613.1459342-1-tristmd@gmail.com/ Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- include/linux/netfilter/x_tables.h | 2 +- net/ipv4/netfilter/arp_tables.c | 9 ++-- net/ipv4/netfilter/ip_tables.c | 9 ++-- net/ipv4/netfilter/iptable_nat.c | 5 +- net/ipv6/netfilter/ip6_tables.c | 9 ++-- net/ipv6/netfilter/ip6table_nat.c | 5 +- net/netfilter/x_tables.c | 81 +++++++++++++++++++++++------- 7 files changed, 83 insertions(+), 37 deletions(-) diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index f7916fd0e807..3aef60abd362 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -299,8 +299,8 @@ struct xt_table *xt_register_table(struct net *net, const struct xt_table *table, struct xt_table_info *bootstrap, struct xt_table_info *newinfo); -void *xt_unregister_table(struct xt_table *table); void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name); +struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name); struct xt_table_info *xt_replace_table(struct xt_table *table, unsigned int num_counters, diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index d19fce858980..f3dadbc416a3 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1501,13 +1501,11 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len static void __arpt_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; + void *loc_cpu_entry; struct arpt_entry *iter; - private = xt_unregister_table(table); - /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; xt_entry_foreach(iter, loc_cpu_entry, private->size) @@ -1515,6 +1513,7 @@ static void __arpt_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int arpt_register_table(struct net *net, @@ -1583,7 +1582,7 @@ int arpt_register_table(struct net *net, void arpt_unregister_table(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_ARP, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_ARP, name); if (table) __arpt_unregister_table(net, table); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 663868cc5f6d..f4079f0718de 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1704,12 +1704,10 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) static void __ipt_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; struct ipt_entry *iter; - - private = xt_unregister_table(table); + void *loc_cpu_entry; /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; @@ -1718,6 +1716,7 @@ static void __ipt_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int ipt_register_table(struct net *net, const struct xt_table *table, @@ -1791,7 +1790,7 @@ int ipt_register_table(struct net *net, const struct xt_table *table, void ipt_unregister_table_exit(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_IPV4, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV4, name); if (table) __ipt_unregister_table(net, table); diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 8fc4912e790d..a0df72554025 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -119,8 +119,11 @@ static int iptable_nat_table_init(struct net *net) } ret = ipt_nat_register_lookups(net); - if (ret < 0) + if (ret < 0) { + xt_unregister_table_pre_exit(net, NFPROTO_IPV4, "nat"); + synchronize_rcu(); ipt_unregister_table_exit(net, "nat"); + } kfree(repl); return ret; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 08abf0df0450..dfaea4f6727e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -1713,12 +1713,10 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) static void __ip6t_unregister_table(struct net *net, struct xt_table *table) { - struct xt_table_info *private; - void *loc_cpu_entry; + struct xt_table_info *private = table->private; struct module *table_owner = table->me; struct ip6t_entry *iter; - - private = xt_unregister_table(table); + void *loc_cpu_entry; /* Decrease module usage counts and free resources */ loc_cpu_entry = private->entries; @@ -1727,6 +1725,7 @@ static void __ip6t_unregister_table(struct net *net, struct xt_table *table) if (private->number > private->initial_entries) module_put(table_owner); xt_free_table_info(private); + kfree(table); } int ip6t_register_table(struct net *net, const struct xt_table *table, @@ -1797,7 +1796,7 @@ int ip6t_register_table(struct net *net, const struct xt_table *table, void ip6t_unregister_table_exit(struct net *net, const char *name) { - struct xt_table *table = xt_find_table(net, NFPROTO_IPV6, name); + struct xt_table *table = xt_unregister_table_exit(net, NFPROTO_IPV6, name); if (table) __ip6t_unregister_table(net, table); diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index bb8aa3fc42b4..c2394e2c94b5 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -121,8 +121,11 @@ static int ip6table_nat_table_init(struct net *net) } ret = ip6t_nat_register_lookups(net); - if (ret < 0) + if (ret < 0) { + xt_unregister_table_pre_exit(net, NFPROTO_IPV6, "nat"); + synchronize_rcu(); ip6t_unregister_table_exit(net, "nat"); + } kfree(repl); return ret; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 2d93f189a79b..76fd0999db4a 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -55,6 +55,9 @@ static struct list_head xt_templates[NFPROTO_NUMPROTO]; struct xt_pernet { struct list_head tables[NFPROTO_NUMPROTO]; + + /* stash area used during netns exit */ + struct list_head dead_tables[NFPROTO_NUMPROTO]; }; struct compat_delta { @@ -1522,23 +1525,6 @@ struct xt_table *xt_register_table(struct net *net, } EXPORT_SYMBOL_GPL(xt_register_table); -void *xt_unregister_table(struct xt_table *table) -{ - struct xt_table_info *private; - - mutex_lock(&xt[table->af].mutex); - private = table->private; - list_del(&table->list); - mutex_unlock(&xt[table->af].mutex); - audit_log_nfcfg(table->name, table->af, private->number, - AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); - kfree(table->ops); - kfree(table); - - return private; -} -EXPORT_SYMBOL_GPL(xt_unregister_table); - /** * xt_unregister_table_pre_exit - pre-shutdown unregister of a table * @net: network namespace @@ -1548,6 +1534,14 @@ EXPORT_SYMBOL_GPL(xt_unregister_table); * Unregisters the specified netfilter table from the given network namespace * and also unregisters the hooks from netfilter core: no new packets will be * processed. + * + * This must be called prior to xt_unregister_table_exit() from the pernet + * .pre_exit callback. After this call, the table is no longer visible to + * the get/setsockopt path. In case of rmmod, module exit path must have + * called xt_unregister_template() prior to unregistering pernet ops to + * prevent re-instantiation of the table. + * + * See also: xt_unregister_table_exit() */ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) { @@ -1557,6 +1551,7 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) mutex_lock(&xt[af].mutex); list_for_each_entry(t, &xt_net->tables[af], list) { if (strcmp(t->name, name) == 0) { + list_move(&t->list, &xt_net->dead_tables[af]); mutex_unlock(&xt[af].mutex); if (t->ops) /* nat table registers with nat core, t->ops is NULL. */ @@ -1567,6 +1562,50 @@ void xt_unregister_table_pre_exit(struct net *net, u8 af, const char *name) mutex_unlock(&xt[af].mutex); } EXPORT_SYMBOL(xt_unregister_table_pre_exit); + +/** + * xt_unregister_table_exit - remove a table during namespace teardown + * @net: the network namespace from which to unregister the table + * @af: address family (e.g., NFPROTO_IPV4, NFPROTO_IPV6) + * @name: name of the table to unregister + * + * Completes the unregister process for a table. This must be called from + * the pernet ops .exit callback. This is the second stage after + * xt_unregister_table_pre_exit(). + * + * pair with xt_unregister_table_pre_exit() during namespace shutdown. + * + * Return: the unregistered table or NULL if the table was never + * instantiated. The caller needs to kfree() the table after it + * has removed the family specific matches/targets. + */ +struct xt_table *xt_unregister_table_exit(struct net *net, u8 af, const char *name) +{ + struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); + struct xt_table *table; + + mutex_lock(&xt[af].mutex); + list_for_each_entry(table, &xt_net->dead_tables[af], list) { + struct nf_hook_ops *ops = NULL; + + if (strcmp(table->name, name) != 0) + continue; + + list_del(&table->list); + + audit_log_nfcfg(table->name, table->af, table->private->number, + AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + swap(table->ops, ops); + mutex_unlock(&xt[af].mutex); + + kfree(ops); + return table; + } + mutex_unlock(&xt[af].mutex); + + return NULL; +} +EXPORT_SYMBOL_GPL(xt_unregister_table_exit); #endif #ifdef CONFIG_PROC_FS @@ -2013,8 +2052,10 @@ static int __net_init xt_net_init(struct net *net) struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); int i; - for (i = 0; i < NFPROTO_NUMPROTO; i++) + for (i = 0; i < NFPROTO_NUMPROTO; i++) { INIT_LIST_HEAD(&xt_net->tables[i]); + INIT_LIST_HEAD(&xt_net->dead_tables[i]); + } return 0; } @@ -2023,8 +2064,10 @@ static void __net_exit xt_net_exit(struct net *net) struct xt_pernet *xt_net = net_generic(net, xt_pernet_id); int i; - for (i = 0; i < NFPROTO_NUMPROTO; i++) + for (i = 0; i < NFPROTO_NUMPROTO; i++) { WARN_ON_ONCE(!list_empty(&xt_net->tables[i])); + WARN_ON_ONCE(!list_empty(&xt_net->dead_tables[i])); + } } static struct pernet_operations xt_net_ops = { From 739d5dac7b2da44a756aa4d758ee3f1ccf5a27f1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:18 +0200 Subject: [PATCH 201/697] netfilter: ebtables: move to two-stage removal scheme [ Upstream commit b7f0544d86d439cb946515d2ef6a0a75e8626710 ] Like previous patches for x_tables, follow same pattern in ebtables. We can't reuse xt helpers: ebt_table struct layout is incompatible. table->ops assignment is now done while still holding the ebt mutex to make sure we never expose partially-filled table struct. Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") Reviewed-by: Tristan Madani Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/bridge/netfilter/ebtable_broute.c | 2 +- net/bridge/netfilter/ebtable_filter.c | 2 +- net/bridge/netfilter/ebtable_nat.c | 2 +- net/bridge/netfilter/ebtables.c | 60 +++++++++++++++++---------- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index 741360219552..e6f9e343b41f 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -128,8 +128,8 @@ static int __init ebtable_broute_init(void) static void __exit ebtable_broute_fini(void) { - unregister_pernet_subsys(&broute_net_ops); ebt_unregister_template(&broute_table); + unregister_pernet_subsys(&broute_net_ops); } module_init(ebtable_broute_init); diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index dacd81b12e62..02b6501c15a5 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -109,8 +109,8 @@ static int __init ebtable_filter_init(void) static void __exit ebtable_filter_fini(void) { - unregister_pernet_subsys(&frame_filter_net_ops); ebt_unregister_template(&frame_filter); + unregister_pernet_subsys(&frame_filter_net_ops); } module_init(ebtable_filter_init); diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 0f2a8c6118d4..9985a82555c4 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -109,8 +109,8 @@ static int __init ebtable_nat_init(void) static void __exit ebtable_nat_fini(void) { - unregister_pernet_subsys(&frame_nat_net_ops); ebt_unregister_template(&frame_nat); + unregister_pernet_subsys(&frame_nat_net_ops); } module_init(ebtable_nat_init); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index a04fc1757528..bfaba18f0e6a 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -42,6 +42,7 @@ struct ebt_pernet { struct list_head tables; + struct list_head dead_tables; }; struct ebt_template { @@ -1162,11 +1163,6 @@ static int do_replace(struct net *net, sockptr_t arg, unsigned int len) static void __ebt_unregister_table(struct net *net, struct ebt_table *table) { - mutex_lock(&ebt_mutex); - list_del(&table->list); - mutex_unlock(&ebt_mutex); - audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, - AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, ebt_cleanup_entry, net, NULL); if (table->private->nentries) @@ -1267,13 +1263,15 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table, for (i = 0; i < num_ops; i++) ops[i].priv = table; - list_add(&table->list, &ebt_net->tables); - mutex_unlock(&ebt_mutex); - table->ops = ops; ret = nf_register_net_hooks(net, ops, num_ops); - if (ret) + if (ret) { + synchronize_rcu(); __ebt_unregister_table(net, table); + } else { + list_add(&table->list, &ebt_net->tables); + } + mutex_unlock(&ebt_mutex); audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, AUDIT_XT_OP_REGISTER, GFP_KERNEL); @@ -1339,7 +1337,7 @@ void ebt_unregister_template(const struct ebt_table *t) } EXPORT_SYMBOL(ebt_unregister_template); -static struct ebt_table *__ebt_find_table(struct net *net, const char *name) +void ebt_unregister_table_pre_exit(struct net *net, const char *name) { struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); struct ebt_table *t; @@ -1348,30 +1346,36 @@ static struct ebt_table *__ebt_find_table(struct net *net, const char *name) list_for_each_entry(t, &ebt_net->tables, list) { if (strcmp(t->name, name) == 0) { + list_move(&t->list, &ebt_net->dead_tables); mutex_unlock(&ebt_mutex); - return t; + nf_unregister_net_hooks(net, t->ops, hweight32(t->valid_hooks)); + return; } } mutex_unlock(&ebt_mutex); - return NULL; -} - -void ebt_unregister_table_pre_exit(struct net *net, const char *name) -{ - struct ebt_table *table = __ebt_find_table(net, name); - - if (table) - nf_unregister_net_hooks(net, table->ops, hweight32(table->valid_hooks)); } EXPORT_SYMBOL(ebt_unregister_table_pre_exit); void ebt_unregister_table(struct net *net, const char *name) { - struct ebt_table *table = __ebt_find_table(net, name); + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + struct ebt_table *t; - if (table) - __ebt_unregister_table(net, table); + mutex_lock(&ebt_mutex); + + list_for_each_entry(t, &ebt_net->dead_tables, list) { + if (strcmp(t->name, name) == 0) { + list_del(&t->list); + audit_log_nfcfg(t->name, AF_BRIDGE, t->private->nentries, + AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); + __ebt_unregister_table(net, t); + mutex_unlock(&ebt_mutex); + return; + } + } + + mutex_unlock(&ebt_mutex); } /* userspace just supplied us with counters */ @@ -2556,11 +2560,21 @@ static int __net_init ebt_pernet_init(struct net *net) struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); INIT_LIST_HEAD(&ebt_net->tables); + INIT_LIST_HEAD(&ebt_net->dead_tables); return 0; } +static void __net_exit ebt_pernet_exit(struct net *net) +{ + struct ebt_pernet *ebt_net = net_generic(net, ebt_pernet_id); + + WARN_ON_ONCE(!list_empty(&ebt_net->tables)); + WARN_ON_ONCE(!list_empty(&ebt_net->dead_tables)); +} + static struct pernet_operations ebt_net_ops = { .init = ebt_pernet_init, + .exit = ebt_pernet_exit, .id = &ebt_pernet_id, .size = sizeof(struct ebt_pernet), }; From cc989ef1c04425fd64d025f9c9df50dc031fc21a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:19 +0200 Subject: [PATCH 202/697] netfilter: ebtables: close dangling table module init race [ Upstream commit 92c603fa07bc0d6a17345de3ad7954730b8de44b ] sashiko reported for a related patch: In modules like iptable_raw.c, [..], if register_pernet_subsys() fails, the rollback might call kfree(rawtable_ops) before [..] During this window, could a concurrent userspace process find the globally visible template, trigger table_init(), [..] The table init functions must always register the template last. Otherwise, set/getsockopt can instantiate a table in a namespace while the required pernet ops (contain the destructor) isn't available. This change is also required in x_tables, handled in followup change. Fixes: 87663c39f898 ("netfilter: ebtables: do not hook tables by default") Reviewed-by: Tristan Madani Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/bridge/netfilter/ebtable_broute.c | 12 +++++------- net/bridge/netfilter/ebtable_filter.c | 12 +++++------- net/bridge/netfilter/ebtable_nat.c | 10 ++++------ 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c index e6f9e343b41f..f05c79f215ea 100644 --- a/net/bridge/netfilter/ebtable_broute.c +++ b/net/bridge/netfilter/ebtable_broute.c @@ -112,18 +112,16 @@ static struct pernet_operations broute_net_ops = { static int __init ebtable_broute_init(void) { - int ret = ebt_register_template(&broute_table, broute_table_init); + int ret = register_pernet_subsys(&broute_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&broute_net_ops); - if (ret) { - ebt_unregister_template(&broute_table); - return ret; - } + ret = ebt_register_template(&broute_table, broute_table_init); + if (ret) + unregister_pernet_subsys(&broute_net_ops); - return 0; + return ret; } static void __exit ebtable_broute_fini(void) diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 02b6501c15a5..0fc03b07e62a 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -93,18 +93,16 @@ static struct pernet_operations frame_filter_net_ops = { static int __init ebtable_filter_init(void) { - int ret = ebt_register_template(&frame_filter, frame_filter_table_init); + int ret = register_pernet_subsys(&frame_filter_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&frame_filter_net_ops); - if (ret) { - ebt_unregister_template(&frame_filter); - return ret; - } + ret = ebt_register_template(&frame_filter, frame_filter_table_init); + if (ret) + unregister_pernet_subsys(&frame_filter_net_ops); - return 0; + return ret; } static void __exit ebtable_filter_fini(void) diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 9985a82555c4..8a10375d8909 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -93,16 +93,14 @@ static struct pernet_operations frame_nat_net_ops = { static int __init ebtable_nat_init(void) { - int ret = ebt_register_template(&frame_nat, frame_nat_table_init); + int ret = register_pernet_subsys(&frame_nat_net_ops); if (ret) return ret; - ret = register_pernet_subsys(&frame_nat_net_ops); - if (ret) { - ebt_unregister_template(&frame_nat); - return ret; - } + ret = ebt_register_template(&frame_nat, frame_nat_table_init); + if (ret) + unregister_pernet_subsys(&frame_nat_net_ops); return ret; } From 524b6337277aabb162bc9c3f018e55fc4b8b8882 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 6 May 2026 12:07:20 +0200 Subject: [PATCH 203/697] netfilter: x_tables: close dangling table module init race [ Upstream commit 16bc4b6686b2c112c10e67d6b493adc3607256d3 ] Similar to the previous ebtables patch: template add exposes the table to userspace, we must do this last to rnsure the pernet ops are set up (contain the destructors). Fixes: fdacd57c79b7 ("netfilter: x_tables: never register tables by default") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/ipv4/netfilter/arptable_filter.c | 23 ++++++++++++----------- net/ipv4/netfilter/iptable_filter.c | 23 ++++++++++++----------- net/ipv4/netfilter/iptable_mangle.c | 25 +++++++++++++------------ net/ipv4/netfilter/iptable_raw.c | 22 +++++++++++----------- net/ipv4/netfilter/iptable_security.c | 23 ++++++++++++----------- net/ipv6/netfilter/ip6table_filter.c | 22 +++++++++++----------- net/ipv6/netfilter/ip6table_mangle.c | 23 ++++++++++++----------- net/ipv6/netfilter/ip6table_raw.c | 20 ++++++++++---------- net/ipv6/netfilter/ip6table_security.c | 23 ++++++++++++----------- 9 files changed, 105 insertions(+), 99 deletions(-) diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 382345567a60..370b635e3523 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -58,25 +58,26 @@ static struct pernet_operations arptable_filter_net_ops = { static int __init arptable_filter_init(void) { - int ret = xt_register_template(&packet_filter, - arptable_filter_table_init); - - if (ret < 0) - return ret; + int ret; arpfilter_ops = xt_hook_ops_alloc(&packet_filter, arpt_do_table); - if (IS_ERR(arpfilter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(arpfilter_ops)) return PTR_ERR(arpfilter_ops); - } ret = register_pernet_subsys(&arptable_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, + arptable_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(arpfilter_ops); - return ret; + unregister_pernet_subsys(&arptable_filter_net_ops); + goto err_free; } + return 0; +err_free: + kfree(arpfilter_ops); return ret; } diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 0dea754a9120..672d7da1071d 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -77,26 +77,27 @@ static struct pernet_operations iptable_filter_net_ops = { static int __init iptable_filter_init(void) { - int ret = xt_register_template(&packet_filter, - iptable_filter_table_init); - - if (ret < 0) - return ret; + int ret; filter_ops = xt_hook_ops_alloc(&packet_filter, ipt_do_table); - if (IS_ERR(filter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(filter_ops)) return PTR_ERR(filter_ops); - } ret = register_pernet_subsys(&iptable_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, + iptable_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(filter_ops); - return ret; + unregister_pernet_subsys(&iptable_filter_net_ops); + goto err_free; } return 0; +err_free: + kfree(filter_ops); + return ret; } static void __exit iptable_filter_fini(void) diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 4d3b12492308..13d25d9a4610 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -111,25 +111,26 @@ static struct pernet_operations iptable_mangle_net_ops = { static int __init iptable_mangle_init(void) { - int ret = xt_register_template(&packet_mangler, - iptable_mangle_table_init); - if (ret < 0) - return ret; + int ret; mangle_ops = xt_hook_ops_alloc(&packet_mangler, iptable_mangle_hook); - if (IS_ERR(mangle_ops)) { - xt_unregister_template(&packet_mangler); - ret = PTR_ERR(mangle_ops); - return ret; - } + if (IS_ERR(mangle_ops)) + return PTR_ERR(mangle_ops); ret = register_pernet_subsys(&iptable_mangle_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_mangler, + iptable_mangle_table_init); if (ret < 0) { - xt_unregister_template(&packet_mangler); - kfree(mangle_ops); - return ret; + unregister_pernet_subsys(&iptable_mangle_net_ops); + goto err_free; } + return 0; +err_free: + kfree(mangle_ops); return ret; } diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 6f7afec7954b..2745c22f4034 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -77,24 +77,24 @@ static int __init iptable_raw_init(void) pr_info("Enabling raw table before defrag\n"); } - ret = xt_register_template(table, - iptable_raw_table_init); - if (ret < 0) - return ret; - rawtable_ops = xt_hook_ops_alloc(table, ipt_do_table); - if (IS_ERR(rawtable_ops)) { - xt_unregister_template(table); + if (IS_ERR(rawtable_ops)) return PTR_ERR(rawtable_ops); - } ret = register_pernet_subsys(&iptable_raw_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(table, + iptable_raw_table_init); if (ret < 0) { - xt_unregister_template(table); - kfree(rawtable_ops); - return ret; + unregister_pernet_subsys(&iptable_raw_net_ops); + goto err_free; } + return 0; +err_free: + kfree(rawtable_ops); return ret; } diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index 81175c20ccbe..491894511c54 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -65,25 +65,26 @@ static struct pernet_operations iptable_security_net_ops = { static int __init iptable_security_init(void) { - int ret = xt_register_template(&security_table, - iptable_security_table_init); - - if (ret < 0) - return ret; + int ret; sectbl_ops = xt_hook_ops_alloc(&security_table, ipt_do_table); - if (IS_ERR(sectbl_ops)) { - xt_unregister_template(&security_table); + if (IS_ERR(sectbl_ops)) return PTR_ERR(sectbl_ops); - } ret = register_pernet_subsys(&iptable_security_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&security_table, + iptable_security_table_init); if (ret < 0) { - xt_unregister_template(&security_table); - kfree(sectbl_ops); - return ret; + unregister_pernet_subsys(&iptable_security_net_ops); + goto err_free; } + return 0; +err_free: + kfree(sectbl_ops); return ret; } diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index cf561919bde8..b074fc477676 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -76,25 +76,25 @@ static struct pernet_operations ip6table_filter_net_ops = { static int __init ip6table_filter_init(void) { - int ret = xt_register_template(&packet_filter, - ip6table_filter_table_init); - - if (ret < 0) - return ret; + int ret; filter_ops = xt_hook_ops_alloc(&packet_filter, ip6t_do_table); - if (IS_ERR(filter_ops)) { - xt_unregister_template(&packet_filter); + if (IS_ERR(filter_ops)) return PTR_ERR(filter_ops); - } ret = register_pernet_subsys(&ip6table_filter_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_filter, ip6table_filter_table_init); if (ret < 0) { - xt_unregister_template(&packet_filter); - kfree(filter_ops); - return ret; + unregister_pernet_subsys(&ip6table_filter_net_ops); + goto err_free; } + return 0; +err_free: + kfree(filter_ops); return ret; } diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index 1a758f2bc537..e6ee036a9b2c 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -104,25 +104,26 @@ static struct pernet_operations ip6table_mangle_net_ops = { static int __init ip6table_mangle_init(void) { - int ret = xt_register_template(&packet_mangler, - ip6table_mangle_table_init); - - if (ret < 0) - return ret; + int ret; mangle_ops = xt_hook_ops_alloc(&packet_mangler, ip6table_mangle_hook); - if (IS_ERR(mangle_ops)) { - xt_unregister_template(&packet_mangler); + if (IS_ERR(mangle_ops)) return PTR_ERR(mangle_ops); - } ret = register_pernet_subsys(&ip6table_mangle_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&packet_mangler, + ip6table_mangle_table_init); if (ret < 0) { - xt_unregister_template(&packet_mangler); - kfree(mangle_ops); - return ret; + unregister_pernet_subsys(&ip6table_mangle_net_ops); + goto err_free; } + return 0; +err_free: + kfree(mangle_ops); return ret; } diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 923455921c1d..3b161ee875bc 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -75,24 +75,24 @@ static int __init ip6table_raw_init(void) pr_info("Enabling raw table before defrag\n"); } - ret = xt_register_template(table, ip6table_raw_table_init); - if (ret < 0) - return ret; - /* Register hooks */ rawtable_ops = xt_hook_ops_alloc(table, ip6t_do_table); - if (IS_ERR(rawtable_ops)) { - xt_unregister_template(table); + if (IS_ERR(rawtable_ops)) return PTR_ERR(rawtable_ops); - } ret = register_pernet_subsys(&ip6table_raw_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(table, ip6table_raw_table_init); if (ret < 0) { - kfree(rawtable_ops); - xt_unregister_template(table); - return ret; + unregister_pernet_subsys(&ip6table_raw_net_ops); + goto err_free; } + return 0; +err_free: + kfree(rawtable_ops); return ret; } diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index c44834d93fc7..4bd5d97b8ab6 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -64,25 +64,26 @@ static struct pernet_operations ip6table_security_net_ops = { static int __init ip6table_security_init(void) { - int ret = xt_register_template(&security_table, - ip6table_security_table_init); - - if (ret < 0) - return ret; + int ret; sectbl_ops = xt_hook_ops_alloc(&security_table, ip6t_do_table); - if (IS_ERR(sectbl_ops)) { - xt_unregister_template(&security_table); + if (IS_ERR(sectbl_ops)) return PTR_ERR(sectbl_ops); - } ret = register_pernet_subsys(&ip6table_security_net_ops); + if (ret < 0) + goto err_free; + + ret = xt_register_template(&security_table, + ip6table_security_table_init); if (ret < 0) { - kfree(sectbl_ops); - xt_unregister_template(&security_table); - return ret; + unregister_pernet_subsys(&ip6table_security_net_ops); + goto err_free; } + return 0; +err_free: + kfree(sectbl_ops); return ret; } From c647e2a21bbbaceda6cdb8a44a56f44d231dc4b4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 7 May 2026 11:19:22 +0200 Subject: [PATCH 204/697] netfilter: bridge: eb_tables: close module init race [ Upstream commit 27414ff1b287ea9a2a11675149ec28e05539f3cc ] sashiko reports for unrelated patch: Does the core ebtables initialization in ebtables.c suffer from a similar race? Once nf_register_sockopt() completes, the sockopts are exposed globally. sockopt has to be registered last, just like in ip/ip6/arptables. Fixes: 5b53951cfc85 ("netfilter: ebtables: use net_generic infra") Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/bridge/netfilter/ebtables.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index bfaba18f0e6a..77df9e856c2e 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2583,19 +2583,20 @@ static int __init ebtables_init(void) { int ret; - ret = xt_register_target(&ebt_standard_target); + ret = register_pernet_subsys(&ebt_net_ops); if (ret < 0) return ret; - ret = nf_register_sockopt(&ebt_sockopts); + + ret = xt_register_target(&ebt_standard_target); if (ret < 0) { - xt_unregister_target(&ebt_standard_target); + unregister_pernet_subsys(&ebt_net_ops); return ret; } - ret = register_pernet_subsys(&ebt_net_ops); + ret = nf_register_sockopt(&ebt_sockopts); if (ret < 0) { - nf_unregister_sockopt(&ebt_sockopts); xt_unregister_target(&ebt_standard_target); + unregister_pernet_subsys(&ebt_net_ops); return ret; } From ae8a5c6b0316ce7b3f7b2c8d13daaf2ffa5be6c7 Mon Sep 17 00:00:00 2001 From: Jianpeng Chang Date: Fri, 8 May 2026 09:56:36 +0900 Subject: [PATCH 205/697] kprobes: skip non-symbol addresses in kprobe_add_ksym_blacklist() [ Upstream commit 307abfac04a254c09c5705d816b33354acee97a0 ] When kprobe_add_area_blacklist() iterates through a section like .kprobes.text, the start address may not correspond to a named symbol. On ARM64 with CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS=y (introduced by commit baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS")), the compiler flag -fpatchable-function-entry=4,2 inserts 2 NOPs before each function entry point for ftrace call_ops. These pre-function NOPs sit at the section base address, before the first named function symbol. The compiler emits a $x mapping symbol at offset 0x00 to mark the start of code, but find_kallsyms_symbol() ignores mapping symbols. Without CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS (e.g. defconfig), no pre-function NOPs are inserted, the first function starts at offset 0x00, and the bug does not trigger. This only affects modules that have a .kprobes.text section (i.e. those using the __kprobes annotation). Modules using NOKPROBE_SYMBOL() instead (like kretprobe_example.ko) blacklist exact function addresses via the _kprobe_blacklist section and are not affected. For kprobe_example.ko on ARM64 with -fpatchable-function-entry=4,2, the .kprobes.text section layout is: offset 0x00: $x + 2 NOPs (mapping symbol + ftrace preamble) offset 0x08: handler_post (64 bytes) offset 0x50: handler_pre (68 bytes) kprobe_add_area_blacklist() starts iterating from the section base address (offset 0x00), which only has the $x mapping symbol. kprobe_add_ksym_blacklist() then calls kallsyms_lookup_size_offset() for this address, which goes through: kallsyms_lookup_size_offset() -> module_address_lookup() -> find_kallsyms_symbol() find_kallsyms_symbol() scans all module symbols to find the closest preceding symbol. Since no named text symbol exists at offset 0x00, find_kallsyms_symbol() picks __UNIQUE_ID_vermagic (a .modinfo symbol whose address is in the temporary image) as the "best" match. The computed "size" = next_text_symbol - modinfo_symbol spans across these two unrelated memory regions, creating a blacklist entry with a bogus range of tens of terabytes. Whether this causes a visible failure depends on address randomization, here is what happens on Raspberry Pi 4/5: - On RPi5, the bogus size was ~35 TB. start + size stayed within 64-bit range, so the blacklist entry covered the entire kernel text. register_kprobe() in the module's own init function failed with -EINVAL. - On RPi4, the bogus size was ~75 TB. start + size overflowed 64 bits and wrapped to a small address near zero. The range check (addr >= start && addr < end) then failed because end wrapped around, so the bogus entry was accidentally harmless and kprobes worked by luck. The same bug exists on both machines, but randomization determines whether the integer overflow masks it or not. Fix this by adding notrace to the __kprobes macro. Functions in .kprobes.text are kprobe infrastructure handlers that should never be traced by ftrace. With notrace, the compiler stops inserting them and the non-symbol gap at the section start disappears entirely. Link: https://lore.kernel.org/all/20260506012706.2785785-1-jianpeng.chang.cn@windriver.com/ Fixes: baaf553d3bc3 ("arm64: Implement HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS") Signed-off-by: Jianpeng Chang Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- include/asm-generic/kprobes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/asm-generic/kprobes.h b/include/asm-generic/kprobes.h index 060eab094e5a..5290a2b2e15a 100644 --- a/include/asm-generic/kprobes.h +++ b/include/asm-generic/kprobes.h @@ -14,7 +14,7 @@ static unsigned long __used \ _kbl_addr_##fname = (unsigned long)fname; # define NOKPROBE_SYMBOL(fname) __NOKPROBE_SYMBOL(fname) /* Use this to forbid a kprobes attach on very low level functions */ -# define __kprobes __section(".kprobes.text") +# define __kprobes notrace __section(".kprobes.text") # define nokprobe_inline __always_inline #else # define NOKPROBE_SYMBOL(fname) From 1c24cf1fd67f6702c719ab73499392cb7af956ae Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Fri, 8 May 2026 09:56:36 +0900 Subject: [PATCH 206/697] test_kprobes: clear kprobes between test runs [ Upstream commit ef5581bb30efb939cc2bf093475c6cc85258e5cd ] Running the kprobes sanity tests twice makes all tests fail and eventually crashes the kernel. [root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run ... # Totals: pass:5 fail:0 skip:0 total:5 ok 1 kprobes_test [root@martin-riscv-1 ~]# echo 1 > /sys/kernel/debug/kunit/kprobes_test/run ... # test_kprobe: EXPECTATION FAILED at lib/tests/test_kprobes.c:64 Expected 0 == register_kprobe(&kp), but register_kprobe(&kp) == -22 (0xffffffffffffffea) ... Unable to handle kernel paging request ... The testsuite defines several kprobes and kretprobes as static variables that are preserved across test runs. After register_kprobe and unregister_kprobe, a kprobe contains some leftover data that must be cleared before the kprobe can be registered again. The tests are setting symbol_name to define the probe location. Address and flags must be cleared. The existing code clears some of the probes between subsequent tests, but not between two test runs. The leftover data from a previous test run makes the registrations fail in the next run. Move the cleanups for all kprobes into kprobes_test_init, this function is called before each single test (including the first test of a test run). Link: https://lore.kernel.org/all/20260507134615.1010905-1-martin@kaiser.cx/ Fixes: e44e81c5b90f ("kprobes: convert tests to kunit") Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- lib/tests/test_kprobes.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/tests/test_kprobes.c b/lib/tests/test_kprobes.c index b7582010125c..06e729e4de05 100644 --- a/lib/tests/test_kprobes.c +++ b/lib/tests/test_kprobes.c @@ -12,6 +12,12 @@ #define div_factor 3 +#define KP_CLEAR(_kp) \ +do { \ + (_kp).addr = NULL; \ + (_kp).flags = 0; \ +} while (0) + static u32 rand1, preh_val, posth_val; static u32 (*target)(u32 value); static u32 (*recursed_target)(u32 value); @@ -125,10 +131,6 @@ static void test_kprobes(struct kunit *test) current_test = test; - /* addr and flags should be cleard for reusing kprobe. */ - kp.addr = NULL; - kp.flags = 0; - KUNIT_EXPECT_EQ(test, 0, register_kprobes(kps, 2)); preh_val = 0; posth_val = 0; @@ -226,9 +228,6 @@ static void test_kretprobes(struct kunit *test) struct kretprobe *rps[2] = {&rp, &rp2}; current_test = test; - /* addr and flags should be cleard for reusing kprobe. */ - rp.kp.addr = NULL; - rp.kp.flags = 0; KUNIT_EXPECT_EQ(test, 0, register_kretprobes(rps, 2)); krph_val = 0; @@ -290,8 +289,6 @@ static void test_stacktrace_on_kretprobe(struct kunit *test) unsigned long myretaddr = (unsigned long)__builtin_return_address(0); current_test = test; - rp3.kp.addr = NULL; - rp3.kp.flags = 0; /* * Run the stacktrace_driver() to record correct return address in @@ -352,8 +349,6 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) struct kretprobe *rps[2] = {&rp3, &rp4}; current_test = test; - rp3.kp.addr = NULL; - rp3.kp.flags = 0; //KUNIT_ASSERT_NE(test, myretaddr, stacktrace_driver()); @@ -367,6 +362,18 @@ static void test_stacktrace_on_nested_kretprobe(struct kunit *test) static int kprobes_test_init(struct kunit *test) { + KP_CLEAR(kp); + KP_CLEAR(kp2); + KP_CLEAR(kp_missed); +#ifdef CONFIG_KRETPROBES + KP_CLEAR(rp.kp); + KP_CLEAR(rp2.kp); +#ifdef CONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE + KP_CLEAR(rp3.kp); + KP_CLEAR(rp4.kp); +#endif +#endif + target = kprobe_target; target2 = kprobe_target2; recursed_target = kprobe_recursed_target; From 77e7818eb3473f342bf297b30d6eb8d92f3dfe97 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Wed, 6 May 2026 03:59:19 +0000 Subject: [PATCH 207/697] tcp: Fix imbalanced icsk_accept_queue count. [ Upstream commit 7eca3292cac7c26dad4c236f51ba225c39a0523f ] When TCP socket migration happens in reqsk_timer_handler(), @sk_listener will be updated with the new listener. When we call __inet_csk_reqsk_queue_drop(), the listener must be the one stored in req->rsk_listener. The cited commit accidentally replaced oreq->rsk_listener with sk_listener, leading to imbalanced icsk_accept_queue count. Let's pass the correct listener to __inet_csk_reqsk_queue_drop(). Fixes: e8c526f2bdf1 ("tcp/dccp: Don't use timer_pending() in reqsk_queue_unlink().") Reported-by: Damiano Melotti Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260506035954.1563147-3-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/inet_connection_sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index ed773cd48876..c777895a720e 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -1149,7 +1149,7 @@ static void reqsk_timer_handler(struct timer_list *t) } drop: - __inet_csk_reqsk_queue_drop(sk_listener, oreq, true); + __inet_csk_reqsk_queue_drop(oreq->rsk_listener, oreq, true); reqsk_put(oreq); } From bfe08fe5624b341aa2e23a1be0e3c6d10a9456ac Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Wed, 6 May 2026 09:08:08 +0000 Subject: [PATCH 208/697] net: napi: Avoid gro timer misfiring at end of busypoll [ Upstream commit 58e2330bd45572a6e3d46ea94cf7a9641f43591a ] When in irq deferral mode (defer-hard-irqs > 0), a short enough gro-flush timeout can trigger before NAPI_STATE_SCHED is cleared if the last poll in busy_poll_stop() takes too long. This can have the effect of leaving the queue stuck with interrupts disabled and no timer armed which results in a tx timeout if there is no subsequent busypoll cycle. To prevent this, defer the gro-flush timer arm after the last poll. Fixes: 7fd3253a7de6 ("net: Introduce preferred busy-polling") Co-developed-by: Martin Karsten Signed-off-by: Martin Karsten Signed-off-by: Dragos Tatulea Reviewed-by: Tariq Toukan Reviewed-by: Cosmin Ratiu Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260506090808.820559-2-dtatulea@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/dev.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 29f2f35ae5eb..681d7de89c50 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6777,9 +6777,9 @@ static void skb_defer_free_flush(void) #if defined(CONFIG_NET_RX_BUSY_POLL) -static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) +static void __busy_poll_stop(struct napi_struct *napi, unsigned long timeout) { - if (!skip_schedule) { + if (!timeout) { gro_normal_list(&napi->gro); __napi_schedule(napi); return; @@ -6789,6 +6789,8 @@ static void __busy_poll_stop(struct napi_struct *napi, bool skip_schedule) gro_flush_normal(&napi->gro, HZ >= 1000); clear_bit(NAPI_STATE_SCHED, &napi->state); + hrtimer_start(&napi->timer, ns_to_ktime(timeout), + HRTIMER_MODE_REL_PINNED); } enum { @@ -6800,8 +6802,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, unsigned flags, u16 budget) { struct bpf_net_context __bpf_net_ctx, *bpf_net_ctx; - bool skip_schedule = false; - unsigned long timeout; + unsigned long timeout = 0; int rc; /* Busy polling means there is a high chance device driver hard irq @@ -6821,10 +6822,12 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, if (flags & NAPI_F_PREFER_BUSY_POLL) { napi->defer_hard_irqs_count = napi_get_defer_hard_irqs(napi); - timeout = napi_get_gro_flush_timeout(napi); - if (napi->defer_hard_irqs_count && timeout) { - hrtimer_start(&napi->timer, ns_to_ktime(timeout), HRTIMER_MODE_REL_PINNED); - skip_schedule = true; + if (napi->defer_hard_irqs_count) { + /* A short enough gro flush timeout and long enough + * poll can result in timer firing too early. + * Timer will be armed later if necessary. + */ + timeout = napi_get_gro_flush_timeout(napi); } } @@ -6839,7 +6842,7 @@ static void busy_poll_stop(struct napi_struct *napi, void *have_poll_lock, trace_napi_poll(napi, rc, budget); netpoll_poll_unlock(have_poll_lock); if (rc == budget) - __busy_poll_stop(napi, skip_schedule); + __busy_poll_stop(napi, timeout); bpf_net_ctx_clear(bpf_net_ctx); local_bh_enable(); } From a248793f00ab589d26cdf603ea09487b1533cd17 Mon Sep 17 00:00:00 2001 From: Mohsin Bashir Date: Wed, 6 May 2026 16:37:45 -0700 Subject: [PATCH 209/697] net: shaper: Reject reparenting of existing nodes [ Upstream commit a77d5a069d959dc45f5f472d48cba37d8cba0f1c ] When an existing node-scope shaper is moved to a different parent via the group operation, the framework fails to update the leaves count on both the old and new parent shapers. Only newly created nodes (handle.id == NET_SHAPER_ID_UNSPEC) trigger the parent leaves increment at line 1039. This causes the parent's leaves counter to diverge from the actual number of children in the xarray. When the node is later deleted, pre_del_node() allocates an array sized by the stale leaves count, but the xarray iteration finds more children than expected, hitting the WARN_ON_ONCE guard and returning -EINVAL. Rather than adding reparenting support with complex leaves count bookkeeping, reject group calls that attempt to change an existing node's parent. Updates to an existing node's rate or leaves under the same parent remain permitted. We expect that for any modification of the topology user should always create new groups and let the kernel garbage collect the leaf-less nodes. Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") Signed-off-by: Mohsin Bashir Link: https://patch.msgid.link/20260506233745.111895-1-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index be9999ab62e3..e41a82241230 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -964,15 +964,22 @@ static int __net_shaper_group(struct net_shaper_binding *binding, int i, ret; if (node->handle.scope == NET_SHAPER_SCOPE_NODE) { + struct net_shaper *cur = NULL; + new_node = node->handle.id == NET_SHAPER_ID_UNSPEC; - if (!new_node && !net_shaper_lookup(binding, &node->handle)) { - /* The related attribute is not available when - * reaching here from the delete() op. - */ - NL_SET_ERR_MSG_FMT(extack, "Node shaper %d:%d does not exists", - node->handle.scope, node->handle.id); - return -ENOENT; + if (!new_node) { + cur = net_shaper_lookup(binding, &node->handle); + if (!cur) { + /* The related attribute is not available + * when reaching here from the delete() op. + */ + NL_SET_ERR_MSG_FMT(extack, + "Node shaper %d:%d does not exist", + node->handle.scope, + node->handle.id); + return -ENOENT; + } } /* When unspecified, the node parent scope is inherited from @@ -986,6 +993,15 @@ static int __net_shaper_group(struct net_shaper_binding *binding, return ret; } + if (cur && net_shaper_handle_cmp(&cur->parent, + &node->parent)) { + NL_SET_ERR_MSG_FMT(extack, + "Cannot reparent node shaper %d:%d", + node->handle.scope, + node->handle.id); + return -EOPNOTSUPP; + } + } else { net_shaper_default_parent(&node->handle, &node->parent); } From eb5991d4c8ba2e8153dfcda3e66a9608377b7dce Mon Sep 17 00:00:00 2001 From: Emil Tantilov Date: Wed, 6 May 2026 14:48:12 -0700 Subject: [PATCH 210/697] idpf: fix read_dev_clk_lock spinlock init in idpf_ptp_init() [ Upstream commit da4f76b6a84ede14a71282ef841768299ead0221 ] In idpf_ptp_init(), read_dev_clk_lock is initialized after ptp_schedule_worker() had already been called (and after idpf_ptp_settime64() could reach the lock). The PTP aux worker fires immediately upon scheduling and can call into idpf_ptp_read_src_clk_reg_direct(), which takes spin_lock(&ptp->read_dev_clk_lock) on an uninitialized lock, triggering the lockdep "non-static key" warning: [12973.796587] idpf 0000:83:00.0: Device HW Reset initiated [12974.094507] INFO: trying to register non-static key. ... [12974.097208] Call Trace: [12974.097213] [12974.097218] dump_stack_lvl+0x93/0xe0 [12974.097234] register_lock_class+0x4c4/0x4e0 [12974.097249] ? __lock_acquire+0x427/0x2290 [12974.097259] __lock_acquire+0x98/0x2290 [12974.097272] lock_acquire+0xc6/0x310 [12974.097281] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097311] ? lockdep_hardirqs_on_prepare+0xde/0x190 [12974.097318] ? finish_task_switch.isra.0+0xd2/0x350 [12974.097330] ? __pfx_ptp_aux_kworker+0x10/0x10 [ptp] [12974.097343] _raw_spin_lock+0x30/0x40 [12974.097353] ? idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097373] idpf_ptp_read_src_clk_reg+0xb7/0x150 [idpf] [12974.097391] ? kthread_worker_fn+0x88/0x3d0 [12974.097404] ? kthread_worker_fn+0x4e/0x3d0 [12974.097411] idpf_ptp_update_cached_phctime+0x26/0x120 [idpf] [12974.097428] ? _raw_spin_unlock_irq+0x28/0x50 [12974.097436] idpf_ptp_do_aux_work+0x15/0x20 [idpf] [12974.097454] ptp_aux_kworker+0x20/0x40 [ptp] [12974.097464] kthread_worker_fn+0xd5/0x3d0 [12974.097474] ? __pfx_kthread_worker_fn+0x10/0x10 [12974.097482] kthread+0xf4/0x130 [12974.097489] ? __pfx_kthread+0x10/0x10 [12974.097498] ret_from_fork+0x32c/0x410 [12974.097512] ? __pfx_kthread+0x10/0x10 [12974.097519] ret_from_fork_asm+0x1a/0x30 [12974.097540] Move the call to spin_lock_init() up a bit to make sure read_dev_clk_lock is not touched before it's been initialized. Fixes: 5cb8805d2366 ("idpf: negotiate PTP capabilities and get PTP clock") Signed-off-by: Emil Tantilov Reviewed-by: Madhu Chittim Reviewed-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Samuel Salin Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-3-a5ea4dc837a9@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/idpf/idpf_ptp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/idpf/idpf_ptp.c b/drivers/net/ethernet/intel/idpf/idpf_ptp.c index 0a8b50350b86..31c5593550e1 100644 --- a/drivers/net/ethernet/intel/idpf/idpf_ptp.c +++ b/drivers/net/ethernet/intel/idpf/idpf_ptp.c @@ -949,6 +949,8 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto free_ptp; } + spin_lock_init(&adapter->ptp->read_dev_clk_lock); + err = idpf_ptp_create_clock(adapter); if (err) goto free_ptp; @@ -974,8 +976,6 @@ int idpf_ptp_init(struct idpf_adapter *adapter) goto remove_clock; } - spin_lock_init(&adapter->ptp->read_dev_clk_lock); - pci_dbg(adapter->pdev, "PTP init successful\n"); return 0; From 34ad3c782644710beb3c5548faaf763711d5b975 Mon Sep 17 00:00:00 2001 From: Marcin Szycik Date: Wed, 6 May 2026 14:48:14 -0700 Subject: [PATCH 211/697] ice: fix setting RSS VSI hash for E830 [ Upstream commit b3cda96feb60d91fe88d52b974ff110dcfa91239 ] ice_set_rss_hfunc() performs a VSI update, in which it sets hashing function, leaving other VSI options unchanged. However, ::q_opt_flags is mistakenly set to the value of another field, instead of its original value, probably due to a typo. What happens next is hardware-dependent: On E810, only the first bit is meaningful (see ICE_AQ_VSI_Q_OPT_PE_FLTR_EN) and can potentially end up in a different state than before VSI update. On E830, some of the remaining bits are not reserved. Setting them to some unrelated values can cause the firmware to reject the update because of invalid settings, or worse - succeed. Reproducer: sudo ethtool -X $PF1 equal 8 Output in dmesg: Failed to configure RSS hash for VSI 6, error -5 Fixes: 352e9bf23813 ("ice: enable symmetric-xor RSS for Toeplitz hash function") Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemek Kitszel Signed-off-by: Marcin Szycik Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-5-a5ea4dc837a9@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index d42b65e29e48..dc5d821bf334 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -8075,7 +8075,7 @@ int ice_set_rss_hfunc(struct ice_vsi *vsi, u8 hfunc) ctx->info.q_opt_rss |= FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hfunc); ctx->info.q_opt_tc = vsi->info.q_opt_tc; - ctx->info.q_opt_flags = vsi->info.q_opt_rss; + ctx->info.q_opt_flags = vsi->info.q_opt_flags; err = ice_update_vsi(hw, vsi->idx, ctx, NULL); if (err) { From d91a9a0496986eac19ab142b38938b6ec43310fb Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 6 May 2026 14:48:15 -0700 Subject: [PATCH 212/697] ice: fix locking in ice_dcb_rebuild() [ Upstream commit 0ded1f36ba4021cba50513e80be6b6e173710168 ] Move the mutex_lock() call up to prevent that DCB settings change after the first ice_query_port_ets() call. The second ice_query_port_ets() call in ice_dcb_rebuild() is already protected by pf->tc_mutex. This also fixes a bug in an error path, as before taking the first "goto dcb_error" in the function jumped over mutex_lock() to mutex_unlock(). This bug has been detected by the clang thread-safety analyzer. Cc: intel-wired-lan@lists.osuosl.org Fixes: 242b5e068b25 ("ice: Fix DCB rebuild after reset") Signed-off-by: Bart Van Assche Reviewed-by: Aleksandr Loktionov Reviewed-by: Przemek Kitszel Tested-by: Arpana Arland Signed-off-by: Jacob Keller Link: https://patch.msgid.link/20260506-jk-iwl-net-2026-05-04-v2-6-a5ea4dc837a9@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_dcb_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c index 9fc8681cc58e..270f5a00ece3 100644 --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c @@ -537,14 +537,14 @@ void ice_dcb_rebuild(struct ice_pf *pf) struct ice_dcbx_cfg *err_cfg; int ret; + mutex_lock(&pf->tc_mutex); + ret = ice_query_port_ets(pf->hw.port_info, &buf, sizeof(buf), NULL); if (ret) { dev_err(dev, "Query Port ETS failed\n"); goto dcb_error; } - mutex_lock(&pf->tc_mutex); - if (!pf->hw.port_info->qos_cfg.is_sw_lldp) ice_cfg_etsrec_defaults(pf->hw.port_info); From 7df3e1dfee5362fddee7b13585055403192f57ca Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 6 May 2026 21:43:11 +0900 Subject: [PATCH 213/697] net: lan966x: avoid unregistering netdev on register failure [ Upstream commit c4f3d6eb1fcf6cd9ce4644f604d5aad1ce594dfc ] lan966x_probe_port() stores the newly allocated net_device in the port before calling register_netdev(). If register_netdev() fails, the probe error path calls lan966x_cleanup_ports(), which sees port->dev and calls unregister_netdev() for a device that was never registered. Destroy the phylink instance created for this port and clear port->dev before returning the registration error. The common cleanup path now skips ports without port->dev before reaching the registered netdev cleanup, so it only handles ports that reached the registered-netdev lifetime. This also avoids treating an uninitialized FDMA netdev and the failed port as a NULL == NULL match in the common cleanup path. Fixes: d28d6d2e37d1 ("net: lan966x: add port module support") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260506124331.31945-1-mhun512@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microchip/lan966x/lan966x_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 47752d3fde0b..1179a6e127c5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -749,11 +749,10 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x) for (p = 0; p < lan966x->num_phys_ports; p++) { port = lan966x->ports[p]; - if (!port) + if (!port || !port->dev) continue; - if (port->dev) - unregister_netdev(port->dev); + unregister_netdev(port->dev); lan966x_xdp_port_deinit(port); if (lan966x->fdma && lan966x->fdma_ndev == port->dev) @@ -873,6 +872,9 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p, err = register_netdev(dev); if (err) { dev_err(lan966x->dev, "register_netdev failed\n"); + phylink_destroy(phylink); + port->phylink = NULL; + port->dev = NULL; return err; } From 994358adc0982d17731ffea7dfacd25afcc89773 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Thu, 7 May 2026 01:28:13 +0530 Subject: [PATCH 214/697] net: ti: icssm-prueth: fix eth_ports_node leak in probe [ Upstream commit 6635fa84403c3a59455b66007c019a7cc632db30 ] The error path on of_property_read_u32() failure inside icssm_prueth_probe() returns without putting eth_ports_node, which was acquired before the for_each_child_of_node() loop. Drop it before returning. Fixes: 511f6c1ae093 ("net: ti: icssm-prueth: Adds ICSSM Ethernet driver") Signed-off-by: Shitalkumar Gandhi Link: https://patch.msgid.link/20260506195813.641610-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/ti/icssm/icssm_prueth.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/ti/icssm/icssm_prueth.c b/drivers/net/ethernet/ti/icssm/icssm_prueth.c index 293b7af04263..cc92f2068584 100644 --- a/drivers/net/ethernet/ti/icssm/icssm_prueth.c +++ b/drivers/net/ethernet/ti/icssm/icssm_prueth.c @@ -1347,6 +1347,7 @@ static int icssm_prueth_probe(struct platform_device *pdev) dev_err(dev, "%pOF error reading port_id %d\n", eth_node, ret); of_node_put(eth_node); + of_node_put(eth_ports_node); return ret; } From e9405f7041271e119134c23438738de1476e7c92 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Sat, 21 Mar 2026 15:42:32 +0100 Subject: [PATCH 215/697] phy: marvell: mvebu-a3700-utmi: fix incorrect USB2_PHY_CTRL register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 91ddf6f722084383fb05be731c0107814b055c0c ] The mvebu_a3700_utmi_phy_power_off() function tries to modify the USB2_PHY_CTRL register by using the IO address of the PHY IP block along with the readl/writel IO accessors. However, the register exist in the USB miscellaneous register space, and as such it must be accessed via regmap like it is done in the mvebu_a3700_utmi_phy_power_on() function. Change the code to use regmap_update_bits() for modífying the register to fix this. Fixes: cc8b7a0ae866 ("phy: add A3700 UTMI PHY driver") Signed-off-by: Gabor Juhos Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260321-a3700-utmi-fix-usb2_phy_ctrl-access-v1-1-6005ff4b5058@gmail.com Signed-off-by: Vinod Koul Signed-off-by: Sasha Levin --- drivers/phy/marvell/phy-mvebu-a3700-utmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c index 04f4fb4bed70..f882bc57649c 100644 --- a/drivers/phy/marvell/phy-mvebu-a3700-utmi.c +++ b/drivers/phy/marvell/phy-mvebu-a3700-utmi.c @@ -168,9 +168,8 @@ static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) u32 reg; /* Disable PHY pull-up and enable USB2 suspend */ - reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); - reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); - writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); + regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), + RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32), 0); /* Power down OTG module */ if (usb32) { From fe59ae27d7346245f5d8d97220f374e63efd28b5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Sun, 19 Apr 2026 14:52:59 -0400 Subject: [PATCH 216/697] NFSD: Fix infinite loop in layout state revocation [ Upstream commit 4f8ef58c10bfe5f86a643c7c8331b37e69e3dae1 ] find_one_sb_stid() skips stids whose sc_status is non-zero, but the SC_TYPE_LAYOUT case in nfsd4_revoke_states() never sets sc_status before calling nfsd4_close_layout(). The retry loop therefore finds the same layout stid on every iteration, hanging the revoker indefinitely. Fixes: 1e33e1414bec ("nfsd: allow layout state to be admin-revoked.") Reported-by: Dai Ngo Reviewed-by: Jeff Layton Tested-by: Dai Ngo Signed-off-by: Chuck Lever Signed-off-by: Sasha Levin --- fs/nfsd/nfs4state.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cb8096e94f51..a9e95df2fdb6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1848,6 +1848,13 @@ void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) break; case SC_TYPE_LAYOUT: ls = layoutstateid(stid); + spin_lock(&clp->cl_lock); + if (stid->sc_status == 0) { + stid->sc_status |= + SC_STATUS_ADMIN_REVOKED; + atomic_inc(&clp->cl_admin_revoked); + } + spin_unlock(&clp->cl_lock); nfsd4_close_layout(ls); break; } From 5afefecfe054c5315a02e2d8c62aada80c93bdb1 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Fri, 8 May 2026 17:32:23 +0800 Subject: [PATCH 217/697] ASoC: sdw_utils: Add quirk to ignore RT712 CODEC_MIC [ Upstream commit 9c37daee7c17fa17e8d41089ee1f658b06cb672a ] Some devices do not use CODEC_MIC but use the host PCH_DMIC instead. Add a quirk to skip the CODEC_MIC DAI when it is not present in disco table, ensuring the correct capture device is used. If CODEC_MIC is present, it continues to be used as default. Fixes: 9489db97f6f0 ("ASoC: sdw_utils: add SmartMic DAI for RT712 VB") Signed-off-by: Mac Chiang Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260508093224.1246282-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 3848c7df1916..050ca17ff105 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -170,6 +170,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, }, }, .dai_num = 3, From 36dc0cea30db09a21512a4094dee2de4cc5ff558 Mon Sep 17 00:00:00 2001 From: Mac Chiang Date: Fri, 8 May 2026 17:32:24 +0800 Subject: [PATCH 218/697] ASoC: sdw_utils: Add quirk to ignore RT721 CODEC_MIC [ Upstream commit fa749a77bdc50f0d695aaf81f1bd55967d77d10f ] Add a quirk to skip the CODEC_MIC DAI when it is not present. This ensures PCH_DMIC is used as the fallback; otherwise, CODEC_MIC remains the default. Fixes: 846a8d3cf3ba ("ASoC: Intel: soc-acpi-intel-ptl-match: Add rt721 support") Signed-off-by: Mac Chiang Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260508093224.1246282-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sdw_utils/soc_sdw_utils.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index 050ca17ff105..3facb78748ac 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -431,6 +431,8 @@ struct asoc_sdw_codec_info codec_info_list[] = { .dai_type = SOC_SDW_DAI_TYPE_MIC, .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, .rtd_init = asoc_sdw_rt_dmic_rtd_init, + .quirk = SOC_SDW_CODEC_MIC, + .quirk_exclude = true, }, }, .dai_num = 3, From 56b4cfcf1518245493c60fd39c56978f508f1816 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Thu, 7 May 2026 16:46:29 +0900 Subject: [PATCH 219/697] fprobe: Fix unregister_fprobe() to wait for RCU grace period [ Upstream commit 657b594b2084b39a4bc6d8493aa2140cb00cea49 ] Commit 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") changed fprobe to register struct fprobe to an rcu-hlist, but it forgot to wait for RCU GP. Thus there can be use-after-free if the fprobe is released right after unregistering. This can be happened on fprobe event and sample module code. To fix this issue, add synchronize_rcu() in unregister_fprobe(). Note that BPF is OK because fprobe is used as a part of bpf_kprobe_multi_link. This unregisters its fprobe in bpf_kprobe_multi_link_release() and it is deallocated via bpf_kprobe_multi_link_dealloc(), which is invoked from bpf_link_defer_dealloc_rcu_gp() RCU callback. For BPF, this also introduced unregister_fprobe_async() which does NOT wait for RCU grace priod. Link: https://lore.kernel.org/all/177813998919.256460.2809243930741138224.stgit@mhiramat.tok.corp.google.com/ Fixes: 4346ba1604093 ("fprobe: Rewrite fprobe on function-graph tracer") Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- include/linux/fprobe.h | 5 +++++ kernel/trace/bpf_trace.c | 3 ++- kernel/trace/fprobe.c | 23 +++++++++++++++++++++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/include/linux/fprobe.h b/include/linux/fprobe.h index 0a3bcd1718f3..be1b38c981d4 100644 --- a/include/linux/fprobe.h +++ b/include/linux/fprobe.h @@ -94,6 +94,7 @@ int register_fprobe(struct fprobe *fp, const char *filter, const char *notfilter int register_fprobe_ips(struct fprobe *fp, unsigned long *addrs, int num); int register_fprobe_syms(struct fprobe *fp, const char **syms, int num); int unregister_fprobe(struct fprobe *fp); +int unregister_fprobe_async(struct fprobe *fp); bool fprobe_is_registered(struct fprobe *fp); int fprobe_count_ips_from_filter(const char *filter, const char *notfilter); #else @@ -113,6 +114,10 @@ static inline int unregister_fprobe(struct fprobe *fp) { return -EOPNOTSUPP; } +static inline int unregister_fprobe_async(struct fprobe *fp) +{ + return -EOPNOTSUPP; +} static inline bool fprobe_is_registered(struct fprobe *fp) { return false; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 70f1292b7ddb..88f470b31375 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2371,7 +2371,8 @@ static void bpf_kprobe_multi_link_release(struct bpf_link *link) struct bpf_kprobe_multi_link *kmulti_link; kmulti_link = container_of(link, struct bpf_kprobe_multi_link, link); - unregister_fprobe(&kmulti_link->fp); + /* Don't wait for RCU GP here. */ + unregister_fprobe_async(&kmulti_link->fp); kprobe_multi_put_modules(kmulti_link->mods, kmulti_link->mods_cnt); } diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index 6419c8d77273..b9346f4efa6d 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -1000,14 +1000,15 @@ static int unregister_fprobe_nolock(struct fprobe *fp) } /** - * unregister_fprobe() - Unregister fprobe. + * unregister_fprobe_async() - Unregister fprobe without RCU GP wait * @fp: A fprobe data structure to be unregistered. * * Unregister fprobe (and remove ftrace hooks from the function entries). + * This function will NOT wait until the fprobe is no longer used. * * Return 0 if @fp is unregistered successfully, -errno if not. */ -int unregister_fprobe(struct fprobe *fp) +int unregister_fprobe_async(struct fprobe *fp) { guard(mutex)(&fprobe_mutex); if (!fp || !fprobe_registered(fp)) @@ -1015,6 +1016,24 @@ int unregister_fprobe(struct fprobe *fp) return unregister_fprobe_nolock(fp); } + +/** + * unregister_fprobe() - Unregister fprobe with RCU GP wait + * @fp: A fprobe data structure to be unregistered. + * + * Unregister fprobe (and remove ftrace hooks from the function entries). + * This function will block until the fprobe is no longer used. + * + * Return 0 if @fp is unregistered successfully, -errno if not. + */ +int unregister_fprobe(struct fprobe *fp) +{ + int ret = unregister_fprobe_async(fp); + + if (!ret) + synchronize_rcu(); + return ret; +} EXPORT_SYMBOL_GPL(unregister_fprobe); static int __init fprobe_initcall(void) From e37ea2c6f17f273813ea4e8e94c102591d598ce1 Mon Sep 17 00:00:00 2001 From: Junyoung Jang Date: Mon, 4 May 2026 20:26:49 +0900 Subject: [PATCH 220/697] fs/statmount: fix slab out-of-bounds write in statmount_mnt_idmap [ Upstream commit a3bf0f28d4ba16e1f35f8c983bb04426b87e2a78 ] statmount_mnt_idmap() writes one mapping with seq_printf() and then manually advances seq->count to include the NUL separator. If seq_printf() overflows, seq_set_overflow() sets seq->count to seq->size. The manual seq->count++ changes this to seq->size + 1. seq_has_overflowed() then no longer detects the overflow. The corrupted count returns to statmount_string(), which later executes: seq->buf[seq->count++] = '\0'; This causes a 1-byte NULL out-of-bounds write on the dynamically allocated seq buffer. Fix this by checking for overflow immediately after seq_printf(). Fixes: 37c4a9590e1e ("statmount: allow to retrieve idmappings") Signed-off-by: Junyoung Jang Link: https://patch.msgid.link/20260504112649.1862936-1-graypanda.inzag@gmail.com Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/mnt_idmapping.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/mnt_idmapping.c b/fs/mnt_idmapping.c index a37991fdb194..3640230a4d43 100644 --- a/fs/mnt_idmapping.c +++ b/fs/mnt_idmapping.c @@ -375,6 +375,8 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_ continue; seq_printf(seq, "%u %u %u", extent->first, lower, extent->count); + if (seq_has_overflowed(seq)) + return -EAGAIN; seq->count++; /* mappings are separated by \0 */ if (seq_has_overflowed(seq)) From ace6b3e033c6337a7a7c5eebe671270f0fa0d377 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Fri, 1 May 2026 15:10:58 +0800 Subject: [PATCH 221/697] fs: Fix return in jfs_mkdir and orangefs_mkdir [ Upstream commit a7cf1da7ac016490d6a1106f2aa6b602d34e9a12 ] Return NULL instead of passing to ERR_PTR while err is zero Fixes these smatch warnings: - fs/jfs/namei.c:311 jfs_mkdir() warn: passing zero to 'ERR_PTR' - fs/orangefs/namei.c:369 orangefs_mkdir() warn: passing zero to 'ERR_PTR' Fixes: 88d5baf69082 ("Change inode_operations.mkdir to return struct dentry *") Signed-off-by: Hongling Zeng Link: https://patch.msgid.link/20260501071058.1243245-1-zenghongling@kylinos.cn Reviewed-by: Jori Koolstra Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/jfs/namei.c | 2 +- fs/orangefs/namei.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 7879c049632b..553fca69cf42 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -308,7 +308,7 @@ static struct dentry *jfs_mkdir(struct mnt_idmap *idmap, struct inode *dip, out1: jfs_info("jfs_mkdir: rc:%d", rc); - return ERR_PTR(rc); + return rc ? ERR_PTR(rc) : NULL; } /* diff --git a/fs/orangefs/namei.c b/fs/orangefs/namei.c index bec5475de094..75e65e72c2d6 100644 --- a/fs/orangefs/namei.c +++ b/fs/orangefs/namei.c @@ -362,7 +362,7 @@ static struct dentry *orangefs_mkdir(struct mnt_idmap *idmap, struct inode *dir, __orangefs_setattr(dir, &iattr); out: op_release(new_op); - return ERR_PTR(ret); + return ret ? ERR_PTR(ret) : NULL; } static int orangefs_rename(struct mnt_idmap *idmap, From 617a2564d8634c06d19097dc0f89bd3a72bcb1b4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Wed, 6 May 2026 01:55:22 -0700 Subject: [PATCH 222/697] irqchip/ath79-cpu: Remove unused function [ Upstream commit 0fa10fb77069fb67aa51384868ef3702b7791465 ] ath79_cpu_irq_init() was part of the legacy pre-OF code that got removed a while back. Remove it to get rid of a missing prototype warning, reported by the kernel test robot. [ tglx: Fix the subject prefix. Sigh ... ] Fixes: 51fa4f8912c0 ("MIPS: ath79: drop legacy IRQ code") Reported-by: kernel test robot Signed-off-by: Rosen Penev Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260506085522.1210143-1-rosenp@gmail.com Closes: https://lore.kernel.org/oe-kbuild-all/202412011509.kGQkDr1y-lkp@intel.com/ Signed-off-by: Sasha Levin --- drivers/irqchip/irq-ath79-cpu.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/irqchip/irq-ath79-cpu.c b/drivers/irqchip/irq-ath79-cpu.c index 923e4bba3776..9b7273a7f8ce 100644 --- a/drivers/irqchip/irq-ath79-cpu.c +++ b/drivers/irqchip/irq-ath79-cpu.c @@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init( } IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", ar79_cpu_intc_of_init); - -void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3) -{ - irq_wb_chan[2] = irq_wb_chan2; - irq_wb_chan[3] = irq_wb_chan3; - mips_cpu_irq_init(); -} From d168a71fc1d6dd3e30d0d66aae098ebcda91db65 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 10 May 2026 22:48:43 +0800 Subject: [PATCH 223/697] ublk: reject max_sectors smaller than PAGE_SECTORS in parameter validation [ Upstream commit 1860c2f85922917d8a46f16a6f4bd2298ffa0fb5 ] blk_validate_limits() requires max_hw_sectors >= PAGE_SECTORS and fires a WARN_ON_ONCE if this invariant is violated. ublk_validate_params() only checked the upper bound of max_sectors against max_io_buf_bytes, allowing userspace to pass small values (including zero) that trigger the warning when blk_mq_alloc_disk() is called from ublk_ctrl_start_dev(). Before 494ea040bcb5, ublk used blk_queue_max_hw_sectors() which silently clamped small values up to PAGE_SECTORS. The conversion to passing queue_limits directly to blk_mq_alloc_disk() lost that clamping and now hits blk_validate_limits()'s WARN_ON_ONCE instead. Validate that max_sectors is at least PAGE_SECTORS in ublk_validate_params() so invalid values are rejected early with -EINVAL instead of reaching the block layer. Fixes: 494ea040bcb5 ("ublk: pass queue_limits to blk_mq_alloc_disk") Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260510144843.769031-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/block/ublk_drv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 2729b1556e81..c339222513b0 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -601,6 +601,9 @@ static int ublk_validate_params(const struct ublk_device *ub) if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) return -EINVAL; + if (p->max_sectors < PAGE_SECTORS) + return -EINVAL; + if (ublk_dev_is_zoned(ub) && !p->chunk_sectors) return -EINVAL; } else From 06ee55f78fbe2e103cc01c62c18e9c55f3a9b019 Mon Sep 17 00:00:00 2001 From: Zhihao Cheng Date: Thu, 7 May 2026 19:23:01 +0800 Subject: [PATCH 224/697] nsfs: fix wrong error code returned for pidns ioctls [ Upstream commit 725ecd80688bf3c57ca9205431f2c06174ff0756 ] When executing NS_GET_PID_FROM_PIDNS (or similar pidns ioctls), if the target task cannot be found in the corresponding pid_ns, the error code should be ESRCH instead of ENOTTY. This bug was introduced when the extensible ioctl handling was added. Without proper return, ret would be overwritten by the default case in the extensible ioctl switch statement. Fixes: a1d220d9dafa8 ("nsfs: iterate through mount namespaces") Signed-off-by: Zhihao Cheng Link: https://patch.msgid.link/20260507112301.1042757-1-chengzhihao1@huawei.com Reviewed-by: Yang Erkun Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/nsfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/nsfs.c b/fs/nsfs.c index f22c2a636e8f..e2f9a725883c 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -261,7 +261,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl, else tsk = find_task_by_pid_ns(arg, pid_ns); if (!tsk) - break; + return ret; switch (ioctl) { case NS_GET_PID_FROM_PIDNS: From 18c0456ea2615b1a743a6db739c74411c3b42bc6 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 30 Mar 2026 15:32:29 +0800 Subject: [PATCH 225/697] irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 91840be8f710370607f949a627e070896faeddb8 ] On PREEMPT_RT, non-HARD irq_work runs in per-CPU kthreads via run_irq_workd(), so irq_work_sync() uses rcuwait() to wait for BUSY==0. After irq_work_single() clears BUSY via atomic_cmpxchg(), it still dereferences @work for irq_work_is_hard() and rcuwait_wake_up(). An irq_work_sync() caller on another CPU that enters after BUSY is cleared can observe BUSY==0 immediately, return, and free the work before those accesses complete — causing a use-after-free. Fix this by wrapping run_irq_workd() in guard(rcu)() so that the entire irq_work_single() execution is within an RCU read-side critical section. Then add synchronize_rcu() in irq_work_sync() after rcuwait_wait_event() to ensure the caller waits for the RCU grace period before returning, preventing premature frees. Fixes: 810979682ccc ("irq_work: Allow irq_work_sync() to sleep if irq_work() no IRQ support.") Suggested-by: Sebastian Andrzej Siewior Suggested-by: Steven Rostedt Signed-off-by: Jiayuan Chen Signed-off-by: Thomas Gleixner Reviewed-by: Sebastian Andrzej Siewior Link: https://patch.msgid.link/20260330073234.303732-1-jiayuan.chen@linux.dev Signed-off-by: Sasha Levin --- kernel/irq_work.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/irq_work.c b/kernel/irq_work.c index 73f7e1fd4ab4..bf411656c316 100644 --- a/kernel/irq_work.c +++ b/kernel/irq_work.c @@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work) !arch_irq_work_has_interrupt()) { rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work), TASK_UNINTERRUPTIBLE); + /* + * Ensure irq_work_single() does not access @work + * after removing IRQ_WORK_BUSY. It is always + * accessed within a RCU-read section. + */ + synchronize_rcu(); return; } @@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync); static void run_irq_workd(unsigned int cpu) { + guard(rcu)(); irq_work_run_list(this_cpu_ptr(&lazy_list)); } From fea4b46f84c50caf93c6c0f2a54b1be2edfb4491 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Wed, 6 May 2026 06:16:02 -0700 Subject: [PATCH 226/697] nvme: fix bio leak on mapping failure [ Upstream commit 2279cd9c61a330e5de4d6eb0bc422820dd6fdf36 ] The local bio is always NULL, so we'd leak the bio if the integrity mapping failed. Just get it directly from the request. Fixes: d0d1d522316e91f ("blk-map: provide the bdev to bio if one exists") Reviewed-by: Sagi Grimberg Reviewed-by: John Garry Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/ioctl.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c index c212fa952c0f..5bbaf257fd6c 100644 --- a/drivers/nvme/host/ioctl.c +++ b/drivers/nvme/host/ioctl.c @@ -122,7 +122,6 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; bool has_metadata = meta_buffer && meta_len; - struct bio *bio = NULL; int ret; if (!nvme_ctrl_sgl_supported(ctrl)) @@ -154,8 +153,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, return ret; out_unmap: - if (bio) - blk_rq_unmap_user(bio); + if (req->bio) + blk_rq_unmap_user(req->bio); return ret; } From 9525e3a6fbb1d126a22ab2ee86ddea25af581a7c Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Wed, 29 Apr 2026 16:11:16 +0800 Subject: [PATCH 227/697] nvme-pci: fix use-after-free in nvme_free_host_mem() [ Upstream commit b35a13036755c5803168a7cb93bc66035c3e65b8 ] nvme_free_host_mem() frees dev->hmb_sgt via dma_free_noncontiguous() but never clears the pointer afterward. This leads to a use-after-free if nvme_free_host_mem() is called twice in the same error path. This can happen during nvme_probe() when nvme_setup_host_mem() succeeds in allocating the HMB (setting dev->hmb_sgt) but nvme_set_host_mem() fails with an I/O error: nvme_setup_host_mem() nvme_alloc_host_mem_single() -> sets dev->hmb_sgt nvme_set_host_mem() -> fails with -EIO nvme_free_host_mem() -> frees hmb_sgt, but does NOT NULL it return error nvme_probe() error path: nvme_free_host_mem() -> dev->hmb_sgt is stale, use-after-free The second call dereferences the freed sgt, causing a NULL pointer dereference in iommu_dma_free_noncontiguous() when it accesses sgt->sgl->dma_address (the backing memory has been freed and zeroed). This is reproducible on Thunderbolt-attached NVMe devices (e.g., OWC Envoy Express behind a Dell WD22TB4 dock) where the device intermittently returns I/O errors during HMB setup due to PCIe link instability. BUG: kernel NULL pointer dereference, address: 0000000000000010 RIP: 0010:iommu_dma_free_noncontiguous+0x22/0x80 Call Trace: dma_free_noncontiguous+0x3b/0x130 nvme_free_host_mem+0x30/0xf0 [nvme] nvme_probe.cold+0xcc/0x275 [nvme] local_pci_probe+0x43/0xa0 pci_device_probe+0xeea/0x290 really_probe+0xf9/0x3b0 __driver_probe_device+0x8b/0x170 driver_probe_device+0x24/0xd0 __driver_attach_async_helper+0x6b/0x110 async_run_entry_fn+0x37/0x170 process_one_work+0x1ac/0x3d0 worker_thread+0x1b8/0x360 kthread+0xf7/0x130 ret_from_fork+0x2d8/0x3a0 ret_from_fork_asm+0x1a/0x30 Fix this by setting dev->hmb_sgt to NULL after freeing it, so the second call takes the multi-descriptor path which safely handles the already-cleaned-up state. Fixes: 63a5c7a4b4c4 ("nvme-pci: use dma_alloc_noncontigous if possible") Signed-off-by: Chia-Lin Kao (AceLan) Signed-off-by: Keith Busch Signed-off-by: Sasha Levin --- drivers/nvme/host/pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 2e32242bed67..5e36a5926fe0 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2320,11 +2320,13 @@ static void nvme_free_host_mem_multi(struct nvme_dev *dev) static void nvme_free_host_mem(struct nvme_dev *dev) { - if (dev->hmb_sgt) + if (dev->hmb_sgt) { dma_free_noncontiguous(dev->dev, dev->host_mem_size, dev->hmb_sgt, DMA_BIDIRECTIONAL); - else + dev->hmb_sgt = NULL; + } else { nvme_free_host_mem_multi(dev); + } dma_free_coherent(dev->dev, dev->host_mem_descs_size, dev->host_mem_descs, dev->host_mem_descs_dma); From eba8af785fde3cb2e9aeabd3fe66a478827eda72 Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Wed, 29 Apr 2026 22:58:15 +0200 Subject: [PATCH 228/697] zonefs: handle integer overflow in zonefs_fname_to_fno [ Upstream commit 3a8389d42bdf4213730f4067f8bfa78bae6564ef ] In zonefs the file name in one of the two directories corresponds to the zone number. Here Alexey reported a possible integer overflow in zonefs_fname_to_fno(), where the parsing of the zone number from the file name can overflow the 'long' data type. Add a check for integer overflows and if the fno 'long' did overflow return -ENOENT. Reported-by: Alexey Dobriyan Fixes: d207794ababe ("zonefs: Dynamically create file inodes when needed") Signed-off-by: Johannes Thumshirn Signed-off-by: Damien Le Moal Signed-off-by: Sasha Levin --- fs/zonefs/super.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c index 70be0b3dda49..7e345a6aa551 100644 --- a/fs/zonefs/super.c +++ b/fs/zonefs/super.c @@ -610,10 +610,14 @@ static long zonefs_fname_to_fno(const struct qstr *fname) return c - '0'; for (i = 0, rname = name + len - 1; i < len; i++, rname--) { + long digit; + c = *rname; if (!isdigit(c)) return -ENOENT; - fno += (c - '0') * shift; + digit = (c - '0') * shift; + if (check_add_overflow(fno, digit, &fno)) + return -ENOENT; shift *= 10; } From 510db031ba6eb40134f84c90ef963ea4b6dfb878 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Fri, 8 May 2026 12:08:46 +0000 Subject: [PATCH 229/697] tcp: Fix out-of-bounds access for twsk in tcp_ao_established_key(). [ Upstream commit 03cb001ef87b3f8d859cf7f96329acf3d6235d29 ] lockdep_sock_is_held() was added in tcp_ao_established_key() by the cited commit. It can be called from tcp_v[46]_timewait_ack() with twsk. Since it does not have sk->sk_lock, the lockdep annotation results in out-of-bound access. $ pahole -C tcp_timewait_sock vmlinux | grep size /* size: 288, cachelines: 5, members: 8 */ $ pahole -C sock vmlinux | grep sk_lock socket_lock_t sk_lock; /* 440 192 */ Let's not use lockdep_sock_is_held() for TCP_TIME_WAIT. Fixes: 6b2d11e2d8fc ("net/tcp: Add missing lockdep annotations for TCP-AO hlist traversals") Reported-by: Damiano Melotti Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260508120853.4098365-1-kuniyu@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/tcp_ao.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 849a69c1f497..aa624434b555 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -116,7 +116,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk, { struct tcp_ao_key *key; - hlist_for_each_entry_rcu(key, &ao->head, node, lockdep_sock_is_held(sk)) { + hlist_for_each_entry_rcu(key, &ao->head, node, + sk_fullsock(sk) && lockdep_sock_is_held(sk)) { if ((sndid >= 0 && key->sndid != sndid) || (rcvid >= 0 && key->rcvid != rcvid)) continue; From aed60070ed7b072f71f048c99e6e76145ddc1224 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Mon, 11 May 2026 10:36:36 -0500 Subject: [PATCH 230/697] ASoC: SOF: amd: Fix error code handling in psp_send_cmd() [ Upstream commit 2c7b1227e582e88db7917412dca4e752c1aff691 ] The smn_read_register() helper returns negative error codes on failure or the register value on success. When used with read_poll_timeout(), the return value is stored in the 'data' variable. Currently 'data' is declared as u32, which causes negative error codes to be cast to large positive values. This makes the condition 'data > 0' incorrectly treat errors as success. Fix by changing 'data' from u32 to int, matching the pattern used in psp_mbox_ready() which correctly handles the same helper function. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-sound/agGES8vWrLOrBu28@stanley.mountain/ Fixes: f120cf33d232 ("ASoC: SOF: amd: Use AMD_NODE") Signed-off-by: Mario Limonciello Link: https://patch.msgid.link/20260511153638.724810-1-mario.limonciello@amd.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/sof/amd/acp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c index 71a18f156de2..f615b8d1c802 100644 --- a/sound/soc/sof/amd/acp.c +++ b/sound/soc/sof/amd/acp.c @@ -223,7 +223,7 @@ static int psp_send_cmd(struct acp_dev_data *adata, int cmd) { struct snd_sof_dev *sdev = adata->dev; int ret; - u32 data; + int data; if (!cmd) return -EINVAL; From 822bb1614ec4198562a6ce86dbcbab684e260595 Mon Sep 17 00:00:00 2001 From: Ally Heev Date: Sun, 16 Nov 2025 19:55:44 +0530 Subject: [PATCH 231/697] powerpc: 82xx: fix uninitialized pointers with free attribute [ Upstream commit acd1e47db03d4b528fd5efb8565dd0de1c79f62a ] Uninitialized pointers with `__free` attribute can cause undefined behavior as the memory allocated to the pointer is freed automatically when the pointer goes out of scope. powerpc/km82xx doesn't have any bugs related to this as of now, but, it is better to initialize and assign pointers with `__free` attribute in one statement to ensure proper scope-based cleanup Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/aPiG_F5EBQUjZqsl@stanley.mountain/ Signed-off-by: Ally Heev Fixes: 4aa5cc1e0012 ("powerpc-km82xx.c: replace of_node_put() with __free") Reviewed-by: Christophe Leroy (CS GROUP) Reviewed-by: Krzysztof Kozlowski Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20251116-aheev-uninitialized-free-attr-km82xx-v2-1-4307e2b5300d@gmail.com Signed-off-by: Sasha Levin --- arch/powerpc/platforms/82xx/km82xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/platforms/82xx/km82xx.c b/arch/powerpc/platforms/82xx/km82xx.c index 99f0f0f41876..4ad223525e89 100644 --- a/arch/powerpc/platforms/82xx/km82xx.c +++ b/arch/powerpc/platforms/82xx/km82xx.c @@ -27,8 +27,8 @@ static void __init km82xx_pic_init(void) { - struct device_node *np __free(device_node); - np = of_find_compatible_node(NULL, NULL, "fsl,pq2-pic"); + struct device_node *np __free(device_node) = of_find_compatible_node(NULL, + NULL, "fsl,pq2-pic"); if (!np) { pr_err("PIC init: can not find cpm-pic node\n"); From 9c6f23cf3a07483ae180a382cea8cf33b45df1f9 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 5 Apr 2026 17:15:45 +0100 Subject: [PATCH 232/697] powerpc: fix dead default for GUEST_STATE_BUFFER_TEST [ Upstream commit aef656a0e6c01796190bb5bd2bdba1c644ed7811 ] The GUEST_STATE_BUFFER_TEST config option should default to KUNIT_ALL_TESTS so that if all tests are enabled then it is included, but currently the 'default KUNIT_ALL_TESTS' statement is shadowed by 'def_tristate n', meaning that this second default statement is currently dead code. It looks to me like the commit 6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") intended to set the default to KUNIT_ALL_TESTS, but mistakenly missed the def_tristate. This dead code was found by kconfirm, a static analysis tool for Kconfig. Fixes: 6ccbbc33f06a ("KVM: PPC: Add helper library for Guest State Buffers") Signed-off-by: Julian Braha Tested-by: Gautam Menghani Reviewed-by: Amit Machhiwal Reviewed-by: Harsh Prateek Bora Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260405161545.161006-1-julianbraha@gmail.com Signed-off-by: Sasha Levin --- arch/powerpc/Kconfig.debug | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index f15e5920080b..e8718bc13eeb 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug @@ -83,11 +83,10 @@ config MSI_BITMAP_SELFTEST depends on DEBUG_KERNEL config GUEST_STATE_BUFFER_TEST - def_tristate n + def_tristate KUNIT_ALL_TESTS prompt "Enable Guest State Buffer unit tests" depends on KUNIT depends on KVM_BOOK3S_HV_POSSIBLE - default KUNIT_ALL_TESTS help The Guest State Buffer is a data format specified in the PAPR. It is by hcalls to communicate the state of L2 guests between From 5366199be46fb53de62861721d34ba816e7e440e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:38 +0100 Subject: [PATCH 233/697] netfs: Fix cancellation of a DIO and single read subrequests [ Upstream commit 6f0f7ac1915abc0d202f0eb4b003a6548a5ba60d ] When the preparation of a new subrequest for a read fails, if the subrequest has already been added to the stream->subrequests list, it can't simply be put and abandoned as the collector may see it. Also, if it hasn't been queued yet, it has two outstanding refs that both need to be put. Both DIO read and single-read dispatch fail at this; further, both differ in the order they do things to the way buffered read works. Fix cancellation of both DIO-read and single-read subrequests that failed preparation by the following steps: (1) Harmonise all three reads (buffered, dio, single) to queue the subreq before prepping it. (2) Make all three call netfs_queue_read() to do the queuing. (3) Set NETFS_RREQ_ALL_QUEUED independently of the queuing as we don't know the length of the subreq at this point. (4) In all cases, set the error and NETFS_SREQ_FAILED flag on the subreq and then call netfs_read_subreq_terminated() to deal with it. This will pass responsibility off to the collector for dealing with it. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-2-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 34 +++++++++++++------------------- fs/netfs/direct_read.c | 42 +++++++++++++--------------------------- fs/netfs/internal.h | 3 +++ fs/netfs/read_collect.c | 11 +++++++++++ fs/netfs/read_single.c | 23 ++++++++++------------ 5 files changed, 50 insertions(+), 63 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 88361e8c7096..10b13924ed54 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -156,9 +156,8 @@ static void netfs_read_cache_to_pagecache(struct netfs_io_request *rreq, netfs_cache_read_terminated, subreq); } -static void netfs_queue_read(struct netfs_io_request *rreq, - struct netfs_io_subrequest *subreq, - bool last_subreq) +void netfs_queue_read(struct netfs_io_request *rreq, + struct netfs_io_subrequest *subreq) { struct netfs_io_stream *stream = &rreq->io_streams[0]; @@ -178,11 +177,6 @@ static void netfs_queue_read(struct netfs_io_request *rreq, } } - if (last_subreq) { - smp_wmb(); /* Write lists before ALL_QUEUED. */ - set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); - } - spin_unlock(&rreq->lock); } @@ -233,6 +227,8 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, subreq->start = start; subreq->len = size; + netfs_queue_read(rreq, subreq); + source = netfs_cache_prepare_read(rreq, subreq, rreq->i_size); subreq->source = source; if (source == NETFS_DOWNLOAD_FROM_SERVER) { @@ -253,6 +249,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, rreq->debug_id, subreq->debug_index, subreq->len, size, subreq->start, ictx->zero_point, rreq->i_size); + netfs_cancel_read(subreq, ret); break; } subreq->len = len; @@ -261,12 +258,7 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, if (rreq->netfs_ops->prepare_read) { ret = rreq->netfs_ops->prepare_read(subreq); if (ret < 0) { - subreq->error = ret; - /* Not queued - release both refs. */ - netfs_put_subrequest(subreq, - netfs_sreq_trace_put_cancel); - netfs_put_subrequest(subreq, - netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } trace_netfs_sreq(subreq, netfs_sreq_trace_prepare); @@ -289,23 +281,23 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, pr_err("Unexpected read source %u\n", source); WARN_ON_ONCE(1); + netfs_cancel_read(subreq, ret); break; issue: slice = netfs_prepare_read_iterator(subreq, ractl); if (slice < 0) { ret = slice; - subreq->error = ret; - trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); - /* Not queued - release both refs. */ - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } - size -= slice; start += slice; + size -= slice; + if (size <= 0) { + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + } - netfs_queue_read(rreq, subreq, size <= 0); netfs_issue_read(rreq, subreq); cond_resched(); } while (size > 0); diff --git a/fs/netfs/direct_read.c b/fs/netfs/direct_read.c index f72e6da88cca..6a8fb0d55e04 100644 --- a/fs/netfs/direct_read.c +++ b/fs/netfs/direct_read.c @@ -45,12 +45,11 @@ static void netfs_prepare_dio_read_iterator(struct netfs_io_subrequest *subreq) * Perform a read to a buffer from the server, slicing up the region to be read * according to the network rsize. */ -static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) +static void netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) { - struct netfs_io_stream *stream = &rreq->io_streams[0]; unsigned long long start = rreq->start; ssize_t size = rreq->len; - int ret = 0; + int ret; do { struct netfs_io_subrequest *subreq; @@ -58,7 +57,10 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) subreq = netfs_alloc_subrequest(rreq); if (!subreq) { - ret = -ENOMEM; + /* Stash the error in the request if there's not + * already an error set. + */ + cmpxchg(&rreq->error, 0, -ENOMEM); break; } @@ -66,25 +68,13 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) subreq->start = start; subreq->len = size; - __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); - - spin_lock(&rreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); - if (list_is_first(&subreq->rreq_link, &stream->subrequests)) { - if (!stream->active) { - stream->collected_to = subreq->start; - /* Store list pointers before active flag */ - smp_store_release(&stream->active, true); - } - } - trace_netfs_sreq(subreq, netfs_sreq_trace_added); - spin_unlock(&rreq->lock); + netfs_queue_read(rreq, subreq); netfs_stat(&netfs_n_rh_download); if (rreq->netfs_ops->prepare_read) { ret = rreq->netfs_ops->prepare_read(subreq); if (ret < 0) { - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); break; } } @@ -113,8 +103,6 @@ static int netfs_dispatch_unbuffered_reads(struct netfs_io_request *rreq) set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); netfs_wake_collector(rreq); } - - return ret; } /* @@ -137,21 +125,17 @@ static ssize_t netfs_unbuffered_read(struct netfs_io_request *rreq, bool sync) // TODO: Use bounce buffer if requested inode_dio_begin(rreq->inode); + netfs_dispatch_unbuffered_reads(rreq); - ret = netfs_dispatch_unbuffered_reads(rreq); - - if (!rreq->submitted) { - netfs_put_request(rreq, netfs_rreq_trace_put_no_submit); - inode_dio_end(rreq->inode); - ret = 0; - goto out; - } + /* The collector will get run, even if we don't manage to submit any + * subreqs, so we shouldn't call inode_dio_end() here. + */ if (sync) ret = netfs_wait_for_read(rreq); else ret = -EIOCBQUEUED; -out: + _leave(" = %zd", ret); return ret; } diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d436e20d3418..645996ecfc80 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -23,6 +23,8 @@ /* * buffered_read.c */ +void netfs_queue_read(struct netfs_io_request *rreq, + struct netfs_io_subrequest *subreq); void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); int netfs_prefetch_for_write(struct file *file, struct folio *folio, size_t offset, size_t len); @@ -108,6 +110,7 @@ static inline void netfs_see_subrequest(struct netfs_io_subrequest *subreq, */ bool netfs_read_collection(struct netfs_io_request *rreq); void netfs_read_collection_worker(struct work_struct *work); +void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error); void netfs_cache_read_terminated(void *priv, ssize_t transferred_or_error); /* diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index e5f6665b3341..d2d902f46627 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -575,6 +575,17 @@ void netfs_read_subreq_terminated(struct netfs_io_subrequest *subreq) } EXPORT_SYMBOL(netfs_read_subreq_terminated); +/* + * Cancel a read subrequest due to preparation failure. + */ +void netfs_cancel_read(struct netfs_io_subrequest *subreq, int error) +{ + trace_netfs_sreq(subreq, netfs_sreq_trace_cancel); + subreq->error = error; + __set_bit(NETFS_SREQ_FAILED, &subreq->flags); + netfs_read_subreq_terminated(subreq); +} + /* * Handle termination of a read from the cache. */ diff --git a/fs/netfs/read_single.c b/fs/netfs/read_single.c index 9d48ced80d1f..cb422de66d0c 100644 --- a/fs/netfs/read_single.c +++ b/fs/netfs/read_single.c @@ -89,7 +89,6 @@ static void netfs_single_read_cache(struct netfs_io_request *rreq, */ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) { - struct netfs_io_stream *stream = &rreq->io_streams[0]; struct netfs_io_subrequest *subreq; int ret = 0; @@ -102,14 +101,7 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) subreq->len = rreq->len; subreq->io_iter = rreq->buffer.iter; - __set_bit(NETFS_SREQ_IN_PROGRESS, &subreq->flags); - - spin_lock(&rreq->lock); - list_add_tail(&subreq->rreq_link, &stream->subrequests); - trace_netfs_sreq(subreq, netfs_sreq_trace_added); - /* Store list pointers before active flag */ - smp_store_release(&stream->active, true); - spin_unlock(&rreq->lock); + netfs_queue_read(rreq, subreq); netfs_single_cache_prepare_read(rreq, subreq); switch (subreq->source) { @@ -121,10 +113,14 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) goto cancel; } + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); rreq->netfs_ops->issue_read(subreq); rreq->submitted += subreq->len; break; case NETFS_READ_FROM_CACHE: + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); trace_netfs_sreq(subreq, netfs_sreq_trace_submit); netfs_single_read_cache(rreq, subreq); rreq->submitted += subreq->len; @@ -134,14 +130,15 @@ static int netfs_single_dispatch_read(struct netfs_io_request *rreq) pr_warn("Unexpected single-read source %u\n", subreq->source); WARN_ON_ONCE(true); ret = -EIO; - break; + goto cancel; } - smp_wmb(); /* Write lists before ALL_QUEUED. */ - set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); return ret; cancel: - netfs_put_subrequest(subreq, netfs_sreq_trace_put_cancel); + netfs_cancel_read(subreq, ret); + smp_wmb(); /* Write lists before ALL_QUEUED. */ + set_bit(NETFS_RREQ_ALL_QUEUED, &rreq->flags); + netfs_wake_collector(rreq); return ret; } From 884c4c4f35e577aba6a0593c80cbea9ca5e6e2b8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:41 +0100 Subject: [PATCH 234/697] netfs: Fix netfs_read_to_pagecache() to pause on subreq failure [ Upstream commit 8a8c0cfdf4658fc5b295b7fc87be56e0d76741f4 ] Fix netfs_read_to_pagecache() so that it pauses the generation of new subrequests if an already-issued subrequest fails. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260425125426.3855807-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-5-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 10b13924ed54..e6157122da3a 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -299,6 +299,11 @@ static void netfs_read_to_pagecache(struct netfs_io_request *rreq, } netfs_issue_read(rreq, subreq); + + if (test_bit(NETFS_RREQ_PAUSE, &rreq->flags)) + netfs_wait_for_paused_read(rreq); + if (test_bit(NETFS_RREQ_FAILED, &rreq->flags)) + break; cond_resched(); } while (size > 0); From b63971238beb79cf701dac33c6cefc56c07c89fa Mon Sep 17 00:00:00 2001 From: Viacheslav Dubeyko Date: Tue, 12 May 2026 13:33:44 +0100 Subject: [PATCH 235/697] netfs: fix VM_BUG_ON_FOLIO() issue in netfs_write_begin() call [ Upstream commit dc7832d05deb4d632e8035e3299e31a3528fa0d0 ] The multiple runs of generic/013 test-case is capable to reproduce a kernel BUG at mm/filemap.c:1504 with probability of 30%. while true; do sudo ./check generic/013 done [ 9849.452376] page: refcount:3 mapcount:0 mapping:00000000e58ff252 index:0x10781 pfn:0x1c322 [ 9849.452412] memcg:ffff8881a1915800 [ 9849.452417] aops:ceph_aops ino:1000058db9e dentry name(?):"f9XXXXXX" [ 9849.452432] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) [ 9849.452441] raw: 0017ffffc0000000 0000000000000000 dead000000000122 ffff88816110d248 [ 9849.452445] raw: 0000000000010781 0000000000000000 00000003ffffffff ffff8881a1915800 [ 9849.452447] page dumped because: VM_BUG_ON_FOLIO(!folio_test_locked(folio)) [ 9849.452474] ------------[ cut here ]------------ [ 9849.452476] kernel BUG at mm/filemap.c:1504! [ 9849.478635] Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI [ 9849.481772] CPU: 2 UID: 0 PID: 84223 Comm: fsstress Not tainted 7.0.0-rc1+ #18 PREEMPT(full) [ 9849.482881] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-9.fc43 06/1 0/2025 [ 9849.484539] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.485076] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.493818] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.495740] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.498678] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.500559] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.501097] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.502108] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.502516] FS: 00007e36cbe94740(0000) GS:ffff88824a899000(0000) knlGS:0000000000000000 [ 9849.502996] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.503810] CR2: 000000c0002b0000 CR3: 000000011bbf6004 CR4: 0000000000772ef0 [ 9849.504459] PKRU: 55555554 [ 9849.504626] Call Trace: [ 9849.505242] [ 9849.505379] netfs_write_begin+0x7c8/0x10a0 [ 9849.505877] ? __kasan_check_read+0x11/0x20 [ 9849.506384] ? __pfx_netfs_write_begin+0x10/0x10 [ 9849.507178] ceph_write_begin+0x8c/0x1c0 [ 9849.507934] generic_perform_write+0x391/0x8f0 [ 9849.508503] ? __pfx_generic_perform_write+0x10/0x10 [ 9849.509062] ? file_update_time_flags+0x19a/0x4b0 [ 9849.509581] ? ceph_get_caps+0x63/0xf0 [ 9849.510259] ? ceph_get_caps+0x63/0xf0 [ 9849.510530] ceph_write_iter+0xe79/0x1ae0 [ 9849.511282] ? __pfx_ceph_write_iter+0x10/0x10 [ 9849.511839] ? lock_acquire+0x1ad/0x310 [ 9849.512334] ? ksys_write+0xf9/0x230 [ 9849.512582] ? lock_is_held_type+0xaa/0x140 [ 9849.513128] vfs_write+0x512/0x1110 [ 9849.513634] ? __fget_files+0x33/0x350 [ 9849.513893] ? __pfx_vfs_write+0x10/0x10 [ 9849.514143] ? mutex_lock_nested+0x1b/0x30 [ 9849.514394] ksys_write+0xf9/0x230 [ 9849.514621] ? __pfx_ksys_write+0x10/0x10 [ 9849.514887] ? do_syscall_64+0x25e/0x1520 [ 9849.515122] ? __kasan_check_read+0x11/0x20 [ 9849.515366] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.515655] __x64_sys_write+0x72/0xd0 [ 9849.515885] ? trace_hardirqs_on+0x24/0x1c0 [ 9849.516130] x64_sys_call+0x22f/0x2390 [ 9849.516341] do_syscall_64+0x12b/0x1520 [ 9849.516545] ? do_syscall_64+0x27c/0x1520 [ 9849.516783] ? do_syscall_64+0x27c/0x1520 [ 9849.517003] ? lock_release+0x318/0x480 [ 9849.517220] ? __x64_sys_io_getevents+0x143/0x2d0 [ 9849.517479] ? percpu_ref_put_many.constprop.0+0x8f/0x210 [ 9849.517779] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.518073] ? do_syscall_64+0x25e/0x1520 [ 9849.518291] ? __kasan_check_read+0x11/0x20 [ 9849.518519] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.518799] ? do_syscall_64+0x27c/0x1520 [ 9849.519024] ? local_clock_noinstr+0xf/0x120 [ 9849.519262] ? entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.519544] ? do_syscall_64+0x25e/0x1520 [ 9849.519781] ? __kasan_check_read+0x11/0x20 [ 9849.520008] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520273] ? do_syscall_64+0x27c/0x1520 [ 9849.520491] ? trace_hardirqs_on_prepare+0x178/0x1c0 [ 9849.520767] ? irqentry_exit+0x10c/0x6c0 [ 9849.520984] ? trace_hardirqs_off+0x86/0x1b0 [ 9849.521224] ? exc_page_fault+0xab/0x130 [ 9849.521472] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 9849.521766] RIP: 0033:0x7e36cbd14907 [ 9849.521989] Code: 10 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f 1f 00 f3 0f 1e fa 64 8b 04 25 18 00 00 00 85 c0 75 10 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 51 c3 48 83 ec 28 48 89 54 24 18 48 89 74 24 [ 9849.523057] RSP: 002b:00007ffff2d2a968 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 [ 9849.523484] RAX: ffffffffffffffda RBX: 000000000000e549 RCX: 00007e36cbd14907 [ 9849.523885] RDX: 000000000000e549 RSI: 00005bd797ec6370 RDI: 0000000000000004 [ 9849.524277] RBP: 0000000000000004 R08: 0000000000000047 R09: 00005bd797ec6370 [ 9849.524652] R10: 0000000000000078 R11: 0000000000000246 R12: 0000000000000049 [ 9849.525062] R13: 0000000010781a37 R14: 00005bd797ec6370 R15: 0000000000000000 [ 9849.525447] [ 9849.525574] Modules linked in: intel_rapl_msr intel_rapl_common intel_uncore_frequency_common intel_pmc_core pmt_telemetry pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec kvm_intel joydev kvm irqbypass ghash_clmulni_intel aesni_intel input_leds rapl mac_hid psmouse vga16fb serio_raw vgastate floppy i2c_piix4 bochs qemu_fw_cfg i2c_smbus pata_acpi sch_fq_codel rbd msr parport_pc ppdev lp parport efi_pstore [ 9849.529150] ---[ end trace 0000000000000000 ]--- [ 9849.529502] RIP: 0010:folio_unlock+0x85/0xa0 [ 9849.530813] Code: 89 df 31 f6 e8 1c f3 ff ff 48 8b 5d f8 c9 31 c0 31 d2 31 f6 31 ff c3 cc cc cc cc 48 c7 c6 80 6c d9 a7 48 89 df e8 4b b3 10 00 <0f> 0b 48 89 df e8 21 e6 2c 00 eb 9d 0f 1f 40 00 66 66 2e 0f 1f 84 [ 9849.534986] RSP: 0018:ffff8881bb8076b0 EFLAGS: 00010246 [ 9849.536198] RAX: 0000000000000000 RBX: ffffea00070c8980 RCX: 0000000000000000 [ 9849.537718] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 [ 9849.539321] RBP: ffff8881bb8076b8 R08: 0000000000000000 R09: 0000000000000000 [ 9849.540862] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000010782000 [ 9849.542438] R13: ffff8881935de738 R14: ffff88816110d010 R15: 0000000000001000 [ 9849.543996] FS: 00007e36cbe94740(0000) GS:ffff88824b899000(0000) knlGS:0000000000000000 [ 9849.545854] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 9849.547092] CR2: 00007e36cb3ff000 CR3: 000000011bbf6006 CR4: 0000000000772ef0 [ 9849.548679] PKRU: 55555554 The race sequence: 1. Read completes -> netfs_read_collection() runs 2. netfs_wake_rreq_flag(rreq, NETFS_RREQ_IN_PROGRESS, ...) 3. netfs_wait_for_read() returns -EFAULT to netfs_write_begin() 4. The netfs_unlock_abandoned_read_pages() unlocks the folio 5. netfs_write_begin() calls folio_unlock(folio) -> VM_BUG_ON_FOLIO() The key reason of the issue that netfs_unlock_abandoned_read_pages() doesn't check the flag NETFS_RREQ_NO_UNLOCK_FOLIO and executes folio_unlock() unconditionally. This patch implements in netfs_unlock_abandoned_read_pages() logic similar to netfs_unlock_read_folio(). Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Signed-off-by: Viacheslav Dubeyko Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-8-dhowells@redhat.com Reviewed-by: Paulo Alcantara (Red Hat) cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org cc: Ceph Development Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/read_retry.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index cca9ac43c077..68fc869513ef 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -288,8 +288,15 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) struct folio *folio = folioq_folio(p, slot); if (folio && !folioq_is_marked2(p, slot)) { - trace_netfs_folio(folio, netfs_folio_trace_abandon); - folio_unlock(folio); + if (folio->index == rreq->no_unlock_folio && + test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, + &rreq->flags)) { + _debug("no unlock"); + } else { + trace_netfs_folio(folio, + netfs_folio_trace_abandon); + folio_unlock(folio); + } } } } From afeb32d9bf9aaeea51d0f723a19f14afb73bd94d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:47 +0100 Subject: [PATCH 236/697] netfs: Fix overrun check in netfs_extract_user_iter() [ Upstream commit 0ef37eef83fad3542ee06db2940433ae1a92b39d ] Fix netfs_extract_user_iter() so that if iov_iter_extract_pages() overfills pages[], then those pages don't get included in the iterator constructed at the end of the function. If there was an overfill, memory corruption has already happened. Fixes: 85dd2c8ff368 ("netfs: Add a function to extract a UBUF or IOVEC into a BVEC iterator") Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-11-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/iterator.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/fs/netfs/iterator.c b/fs/netfs/iterator.c index 429e4396e1b0..b375567e0520 100644 --- a/fs/netfs/iterator.c +++ b/fs/netfs/iterator.c @@ -72,21 +72,24 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, break; } - if (ret > count) { - pr_err("get_pages rc=%zd more than %zu\n", ret, count); + if (WARN(ret > count, + "%s: extract_pages overrun %zd > %zu bytes\n", + __func__, ret, count)) { + ret = -EIO; break; } - count -= ret; - ret += offset; - cur_npages = DIV_ROUND_UP(ret, PAGE_SIZE); - - if (npages + cur_npages > max_pages) { - pr_err("Out of bvec array capacity (%u vs %u)\n", - npages + cur_npages, max_pages); + cur_npages = DIV_ROUND_UP(offset + ret, PAGE_SIZE); + if (WARN(cur_npages > max_pages - npages, + "%s: extract_pages overrun %u > %u pages\n", + __func__, npages + cur_npages, max_pages)) { + ret = -EIO; break; } + count -= ret; + ret += offset; + for (i = 0; i < cur_npages; i++) { len = ret > PAGE_SIZE ? PAGE_SIZE : ret; bvec_set_page(bv + npages + i, *pages++, len - offset, offset); @@ -97,6 +100,11 @@ ssize_t netfs_extract_user_iter(struct iov_iter *orig, size_t orig_len, npages += cur_npages; } + /* Note: Don't try to clean up after EIO. Either we got no pages, so + * nothing to clean up, or we got a buffer overrun, memory corruption + * and can't trust the stuff in the buffer (a WARN was emitted). + */ + if (ret < 0 && (ret == -ENOMEM || npages == 0)) { for (i = 0; i < npages; i++) unpin_user_page(bv[i].bv_page); From fb6ec883b48b8789e5e690dcd440d2db941e840c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:48 +0100 Subject: [PATCH 237/697] netfs: Fix netfs_invalidate_folio() to clear dirty bit if all changes gone [ Upstream commit 156ac2ec2ee77c44c4eb7439d6d165247ba12247 ] If a streaming write is made, this will leave the relevant modified folio in a not-uptodate, but dirty state with a netfs_folio struct hung off of folio->private indicating the dirty range. Subsequently truncating the file such that the dirty data in the folio is removed, but the first part of the folio theoretically remains will cause the netfs_folio struct to be discarded... but will leave the dirty flag set. If the folio is then read via mmap(), netfs_read_folio() will see that the page is dirty and jump to netfs_read_gaps() to fill in the missing bits. netfs_read_gaps(), however, expects there to be a netfs_folio struct present and can oops because truncate removed it. Fix this by calling folio_cancel_dirty() in netfs_invalidate_folio() in the event that all the dirty data in the folio is erased (as nfs does). Also add some tracepoints to log modifications to a dirty page. This can be reproduced with something like: dd if=/dev/zero of=/xfstest.test/foo bs=1M count=1 umount /xfstest.test mount /xfstest.test xfs_io -c "w 0xbbbf 0xf96c" \ -c "truncate 0xbbbf" \ -c "mmap -r 0xb000 0x11000" \ -c "mr 0xb000 0x11000" \ /xfstest.test/foo with fscaching disabled (otherwise streaming writes are suppressed) and a change to netfs_perform_write() to disallow streaming writes if the fd is open O_RDWR: if (//(file->f_mode & FMODE_READ) || <--- comment this out netfs_is_cache_enabled(ctx)) { It should be reproducible even without this change, but if prevents the above trivial xfs_io command from reproducing it. Note that the initial dd is important: the file must start out sufficiently large that the zero-point logic doesn't just clear the gaps because it knows there's nothing in the file to read yet. Unmounting and mounting is needed to clear the pagecache (there are other ways to do that that may also work). This was initially reproduced with the generic/522 xfstest on some patches that remove the FMODE_READ restriction. Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write") Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-12-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/misc.c | 6 +++++- include/trace/events/netfs.h | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index 486166460e17..eb309bef7260 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -256,6 +256,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) /* Move the start of the data. */ finfo->dirty_len = fend - iend; finfo->dirty_offset = offset; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); return; } @@ -264,12 +265,14 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) */ if (iend >= fend) { finfo->dirty_len = offset - fstart; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_tail); return; } /* A partial write was split. The caller has already zeroed * it, so just absorb the hole. */ + trace_netfs_folio(folio, netfs_folio_trace_invalidate_middle); } return; @@ -277,8 +280,9 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) netfs_put_group(netfs_folio_group(folio)); folio_detach_private(folio); folio_clear_uptodate(folio); + folio_cancel_dirty(folio); kfree(finfo); - return; + trace_netfs_folio(folio, netfs_folio_trace_invalidate_all); } EXPORT_SYMBOL(netfs_invalidate_folio); diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index cbe28211106c..88d814ba1e69 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -194,6 +194,10 @@ EM(netfs_folio_trace_copy_to_cache, "mark-copy") \ EM(netfs_folio_trace_end_copy, "end-copy") \ EM(netfs_folio_trace_filled_gaps, "filled-gaps") \ + EM(netfs_folio_trace_invalidate_all, "inval-all") \ + EM(netfs_folio_trace_invalidate_front, "inval-front") \ + EM(netfs_folio_trace_invalidate_middle, "inval-mid") \ + EM(netfs_folio_trace_invalidate_tail, "inval-tail") \ EM(netfs_folio_trace_kill, "kill") \ EM(netfs_folio_trace_kill_cc, "kill-cc") \ EM(netfs_folio_trace_kill_g, "kill-g") \ From 185ded4112cd4e2969ca2b80e83caefb94a353fc Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:49 +0100 Subject: [PATCH 238/697] netfs: Defer the emission of trace_netfs_folio() [ Upstream commit daeb443b92817021c1234e8eded219e164b7c35d ] Change netfs_perform_write() to keep the netfs_folio trace value in a variable and emit it later to make it easier to choose the value displayed. This is a prerequisite for a subsequent patch. Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-13-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Stable-dep-of: 7b4dcf1b9455 ("netfs: Fix streaming write being overwritten") Signed-off-by: Sasha Levin --- fs/netfs/buffered_write.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 09394ac2c180..1fa13c2629a7 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -150,6 +150,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, } do { + enum netfs_folio_trace trace; struct netfs_folio *finfo; struct netfs_group *group; unsigned long long fpos; @@ -223,7 +224,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, if (unlikely(copied == 0)) goto copy_failed; netfs_set_group(folio, netfs_group); - trace_netfs_folio(folio, netfs_folio_is_uptodate); + trace = netfs_folio_is_uptodate; goto copied; } @@ -239,7 +240,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, folio_zero_segment(folio, offset + copied, flen); __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_modify_and_clear); + trace = netfs_modify_and_clear; goto copied; } @@ -257,7 +258,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, } __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_whole_folio_modify); + trace = netfs_whole_folio_modify; goto copied; } @@ -284,7 +285,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, if (unlikely(copied == 0)) goto copy_failed; netfs_set_group(folio, netfs_group); - trace_netfs_folio(folio, netfs_just_prefetch); + trace = netfs_just_prefetch; goto copied; } @@ -298,7 +299,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, if (offset == 0 && copied == flen) { __netfs_set_group(folio, netfs_group); folio_mark_uptodate(folio); - trace_netfs_folio(folio, netfs_streaming_filled_page); + trace = netfs_streaming_filled_page; goto copied; } @@ -313,7 +314,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len = copied; folio_attach_private(folio, (void *)((unsigned long)finfo | NETFS_FOLIO_INFO)); - trace_netfs_folio(folio, netfs_streaming_write); + trace = netfs_streaming_write; goto copied; } @@ -333,9 +334,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, folio_detach_private(folio); folio_mark_uptodate(folio); kfree(finfo); - trace_netfs_folio(folio, netfs_streaming_cont_filled_page); + trace = netfs_streaming_cont_filled_page; } else { - trace_netfs_folio(folio, netfs_streaming_write_cont); + trace = netfs_streaming_write_cont; } goto copied; } @@ -351,6 +352,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, continue; copied: + trace_netfs_folio(folio, trace); flush_dcache_folio(folio); /* Update the inode size if we moved the EOF marker */ From ef9b521212e4863814ef7dfe19889abaf55ca840 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:50 +0100 Subject: [PATCH 239/697] netfs: Fix streaming write being overwritten [ Upstream commit 7b4dcf1b9455a6e52ac7478b4057dbe10359576d ] In order to avoid reading whilst writing, netfslib will allow "streaming writes" in which dirty data is stored directly into folios without reading them first. Such folios are marked dirty but may not be marked uptodate. If a folio is entirely written by a streaming write, uptodate will be set, otherwise it will have a netfs_folio struct attached to ->private recording the dirty region. In the event that a partially written streaming write page is to be overwritten entirely by a single write(), netfs_perform_write() will try to copy over it, but doesn't discard the netfs_folio if it succeeds; further, it doesn't correctly handle a partial copy that overwrites some of the dirty data. Fix this by the following: (1) If the folio is successfully overwritten, free the netfs_folio struct before marking the page uptodate. (2) If the copy to the folio partially fails, but short of the dirty data, just ignore the copy. (3) If the copy partially fails and overwrites some of the dirty data, accept the copy, update the netfs_folio struct to record the new data. If the folio is now filled, free the netfs_folio and set uptodate, otherwise return a partial write. Found with: fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=junk.fsxops using the following as junk.fsxops: truncate 0x0 0 0x927c0 write 0x63fb8 0x53c8 0 copy_range 0xb704 0x19b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * write 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0x00000 0x20000 0x9157c read 0x20000 0x20000 0x9157c read 0x40000 0x20000 0x9157c read 0x60000 0x20000 0x9157c read 0x7e1a0 0xcfb9 0x9157c on cifs with the default cache option. It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write(): if (//(file->f_mode & FMODE_READ) || netfs_is_cache_enabled(ctx)) { and no fscache. This was initially found with the generic/522 xfstest. Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-14-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_write.c | 47 ++++++++++++++++++++++++++---------- include/trace/events/netfs.h | 3 +++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 1fa13c2629a7..99736895e964 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -247,18 +247,38 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, /* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset == 0 && part >= flen) { copied = copy_folio_from_iter_atomic(folio, offset, part, iter); - if (unlikely(copied == 0)) + if (likely(copied == part)) { + if (finfo) { + trace = netfs_whole_folio_modify_filled; + goto folio_now_filled; + } + __netfs_set_group(folio, netfs_group); + folio_mark_uptodate(folio); + trace = netfs_whole_folio_modify; + goto copied; + } + if (copied == 0) goto copy_failed; - if (unlikely(copied < part)) { + if (!finfo || copied <= finfo->dirty_offset) { maybe_trouble = true; iov_iter_revert(iter, copied); copied = 0; folio_unlock(folio); goto retry; } - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace = netfs_whole_folio_modify; + + /* We overwrote some existing dirty data, so we have to + * accept the partial write. + */ + finfo->dirty_len += finfo->dirty_offset; + if (finfo->dirty_len == flen) { + trace = netfs_whole_folio_modify_filled_efault; + goto folio_now_filled; + } + if (copied > finfo->dirty_len) + finfo->dirty_len = copied; + finfo->dirty_offset = 0; + trace = netfs_whole_folio_modify_efault; goto copied; } @@ -328,16 +348,10 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto copy_failed; finfo->dirty_len += copied; if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); - folio_mark_uptodate(folio); - kfree(finfo); trace = netfs_streaming_cont_filled_page; - } else { - trace = netfs_streaming_write_cont; + goto folio_now_filled; } + trace = netfs_streaming_write_cont; goto copied; } @@ -351,6 +365,13 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto out; continue; + folio_now_filled: + if (finfo->netfs_group) + folio_change_private(folio, finfo->netfs_group); + else + folio_detach_private(folio); + folio_mark_uptodate(folio); + kfree(finfo); copied: trace_netfs_folio(folio, trace); flush_dcache_folio(folio); diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index 88d814ba1e69..db045135406c 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -177,6 +177,9 @@ EM(netfs_folio_is_uptodate, "mod-uptodate") \ EM(netfs_just_prefetch, "mod-prefetch") \ EM(netfs_whole_folio_modify, "mod-whole-f") \ + EM(netfs_whole_folio_modify_efault, "mod-whole-f!") \ + EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ + EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ EM(netfs_streaming_write, "mod-streamw") \ EM(netfs_streaming_write_cont, "mod-streamw+") \ From 003aa0dd26c964025acd6d1213bcdbd674db2ca9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:51 +0100 Subject: [PATCH 240/697] netfs: Fix potential deadlock in write-through mode [ Upstream commit b6a4ae1634b3ad2aaa05222e53d36da532852faf ] Fix netfs_advance_writethrough() to always unlock the supplied folio and to mark it dirty if it isn't yet written to the end. Unfortunately, it can't be marked for writeback until the folio is done with as that may cause a deadlock against mmapped reads and writes. Even though it has been marked dirty, premature writeback can't occur as the caller is holding both inode->i_rwsem (which will prevent concurrent truncation, fallocation, DIO and other writes) and ictx->wb_lock (which will cause flushing to wait and writeback to skip or wait). Note that this may be easier to deal with once the queuing of folios is split from the generation of subrequests. Fixes: 288ace2f57c9 ("netfs: New writeback implementation") Closes: https://sashiko.dev/#/patchset/20260427154639.180684-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-15-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/write_issue.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 2db688f94125..9bf05099155d 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -413,12 +413,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, if (streamw) netfs_issue_write(wreq, cache); - /* Flip the page to the writeback state and unlock. If we're called - * from write-through, then the page has already been put into the wb - * state. - */ - if (wreq->origin == NETFS_WRITEBACK) - folio_start_writeback(folio); + folio_start_writeback(folio); folio_unlock(folio); if (fgroup == NETFS_FOLIO_COPY_TO_CACHE) { @@ -646,29 +641,41 @@ int netfs_advance_writethrough(struct netfs_io_request *wreq, struct writeback_c struct folio *folio, size_t copied, bool to_page_end, struct folio **writethrough_cache) { + int ret; + _enter("R=%x ic=%zu ws=%u cp=%zu tp=%u", wreq->debug_id, wreq->buffer.iter.count, wreq->wsize, copied, to_page_end); - if (!*writethrough_cache) { - if (folio_test_dirty(folio)) - /* Sigh. mmap. */ - folio_clear_dirty_for_io(folio); + /* The folio is locked. */ + if (*writethrough_cache != folio) { + if (*writethrough_cache) { + /* Did the folio get moved? */ + folio_put(*writethrough_cache); + *writethrough_cache = NULL; + } /* We can make multiple writes to the folio... */ - folio_start_writeback(folio); if (wreq->len == 0) trace_netfs_folio(folio, netfs_folio_trace_wthru); else trace_netfs_folio(folio, netfs_folio_trace_wthru_plus); *writethrough_cache = folio; + folio_get(folio); } wreq->len += copied; - if (!to_page_end) + + if (!to_page_end) { + folio_mark_dirty(folio); + folio_unlock(folio); return 0; + } + ret = netfs_write_folio(wreq, wbc, folio); + folio_put(*writethrough_cache); *writethrough_cache = NULL; - return netfs_write_folio(wreq, wbc, folio); + wreq->submitted = wreq->len; + return ret; } /* @@ -682,8 +689,12 @@ ssize_t netfs_end_writethrough(struct netfs_io_request *wreq, struct writeback_c _enter("R=%x", wreq->debug_id); - if (writethrough_cache) + if (writethrough_cache) { + folio_lock(writethrough_cache); netfs_write_folio(wreq, wbc, writethrough_cache); + folio_put(writethrough_cache); + wreq->submitted = wreq->len; + } netfs_end_issue_write(wreq); From 0b18cd70ebab7ea13e9883e4e9a62011e65c7d3b Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:52 +0100 Subject: [PATCH 241/697] netfs: Fix read-gaps to remove netfs_folio from filled folio [ Upstream commit a41168aef634356a9b87ec44349e3c82835700a5 ] Fix netfs_read_gaps() to remove the netfs_folio record from the folio record before marking the folio uptodate if it successfully fills the gaps around the dirty data in a streaming write folio (dirty, but not uptodate). Found with: fsx -q -N 1000000 -p 10000 -o 128000 -l 600000 \ /xfstest.test/junk --replay-ops=junk.fsxops using the following as junk.fsxops: truncate 0x0 0x138b1 0x8b15d * write 0x507ee 0x10df7 0x927c0 write 0x19993 0x10e04 0x927c0 * mapwrite 0x66214 0x1a253 0x927c0 copy_range 0xb704 0x89b9 0x24429 0x79380 write 0x2402b 0x144a2 0x90660 * mapwrite 0x204d5 0x140a0 0x927c0 * copy_range 0x1f72c 0x137d0 0x7a906 0x927c0 * read 0 0x9157c 0x9157c on cifs with the default cache option. It shows folio 0x24 misbehaving if the FMODE_READ check is commented out in netfs_perform_write(): if (//(file->f_mode & FMODE_READ) || netfs_is_cache_enabled(ctx)) { and no fscache. This was initially found with the generic/522 xfstest. Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-16-dhowells@redhat.com Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index e6157122da3a..3531c19eea97 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -394,6 +394,7 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) { struct netfs_io_request *rreq; struct address_space *mapping = folio->mapping; + struct netfs_group *group = netfs_folio_group(folio); struct netfs_folio *finfo = netfs_folio_info(folio); struct netfs_inode *ctx = netfs_inode(mapping->host); struct folio *sink = NULL; @@ -460,6 +461,12 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) ret = netfs_wait_for_read(rreq); if (ret >= 0) { + if (group) + folio_change_private(folio, group); + else + folio_detach_private(folio); + kfree(finfo); + trace_netfs_folio(folio, netfs_folio_trace_filled_gaps); flush_dcache_folio(folio); folio_mark_uptodate(folio); } @@ -495,10 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) struct netfs_inode *ctx = netfs_inode(mapping->host); int ret; - if (folio_test_dirty(folio)) { - trace_netfs_folio(folio, netfs_folio_trace_read_gaps); + if (folio_test_dirty(folio)) return netfs_read_gaps(file, folio); - } _enter("%lx", folio->index); From 616578e40dcba3f94810d841c5a52b7e3bc8ede7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:53 +0100 Subject: [PATCH 242/697] netfs: Fix write streaming disablement if fd open O_RDWR [ Upstream commit 70a7b9193bbbfceaab5974de66834c64ccc875dd ] In netfs_perform_write(), "write streaming" (the caching of dirty data in dirty but !uptodate folios) is performed to avoid the need to read data that is just going to get immediately overwritten. However, this is/will be disabled in three circumstances: if the fd is open O_RDWR, if fscache is in use (as we need to round out the blocks for DIO) or if content encryption is enabled (again for rounding out purposes). The idea behind disabling it if the fd is open O_RDWR is that we'd need to flush the write-streaming page before we could read the data, particularly through mmap. But netfs now fills in the gaps if ->read_folio() is called on the page, so that is unnecessary. Further, this doesn't actually work if a separate fd is open for reading. Fix this by removing the check for O_RDWR, thereby allowing streaming writes even when we might read. This caused a number of problems with the generic/522 xfstest, but those are now fixed. Fixes: c38f4e96e605 ("netfs: Provide func to copy data to pagecache for buffered write") Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-17-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_write.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 99736895e964..60215a772357 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -204,11 +204,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, } /* Decide how we should modify a folio. We might be attempting - * to do write-streaming, in which case we don't want to a - * local RMW cycle if we can avoid it. If we're doing local - * caching or content crypto, we award that priority over - * avoiding RMW. If the file is open readably, then we also - * assume that we may want to read what we wrote. + * to do write-streaming, as we don't want to a local RMW cycle + * if we can avoid it. If we're doing local caching or content + * crypto, we award that priority over avoiding RMW. If the + * file is open readably, then we let ->read_folio() fill in + * the gaps. */ finfo = netfs_folio_info(folio); group = netfs_folio_group(folio); @@ -284,12 +284,9 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, /* We don't want to do a streaming write on a file that loses * caching service temporarily because the backing store got - * culled and we don't really want to get a streaming write on - * a file that's open for reading as ->read_folio() then has to - * be able to flush it. + * culled. */ - if ((file->f_mode & FMODE_READ) || - netfs_is_cache_enabled(ctx)) { + if (netfs_is_cache_enabled(ctx)) { if (finfo) { netfs_stat(&netfs_n_wh_wstream_conflict); goto flush_content; From d4f4bc87c76511cf2532448b0fa40c25e894bd7d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:54 +0100 Subject: [PATCH 243/697] netfs: Fix early put of sink folio in netfs_read_gaps() [ Upstream commit 3e5dd91b87a8b1450217b56a336bee315f40da7d ] Fix netfs_read_gaps() to release the sink page it uses after waiting for the request to complete. The way the sink page is used is that an ITER_BVEC-class iterator is created that has the gaps from the target folio at either end, but has the sink page tiled over the middle so that a single read op can fill in both gaps. The bug was found by KASAN detecting a UAF on the generic/075 xfstest in the cifsd kernel thread that handles reception of data from the TCP socket: BUG: KASAN: use-after-free in _copy_to_iter+0x48a/0xa20 Write of size 885 at addr ffff888107f92000 by task cifsd/1285 CPU: 2 UID: 0 PID: 1285 Comm: cifsd Not tainted 7.0.0 #6 PREEMPT(lazy) Call Trace: dump_stack_lvl+0x5d/0x80 print_report+0x17f/0x4f1 kasan_report+0x100/0x1e0 kasan_check_range+0x10f/0x1e0 __asan_memcpy+0x3c/0x60 _copy_to_iter+0x48a/0xa20 __skb_datagram_iter+0x2c9/0x430 skb_copy_datagram_iter+0x6e/0x160 tcp_recvmsg_locked+0xce0/0x1130 tcp_recvmsg+0xeb/0x300 inet_recvmsg+0xcf/0x3a0 sock_recvmsg+0xea/0x100 cifs_readv_from_socket+0x3a6/0x4d0 [cifs] cifs_read_iter_from_socket+0xdd/0x130 [cifs] cifs_readv_receive+0xaad/0xb10 [cifs] cifs_demultiplex_thread+0x1148/0x1740 [cifs] kthread+0x1cf/0x210 Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Reported-by: Steve French Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-18-dhowells@redhat.com Reviewed-by: Paulo Alcantara (Red Hat) cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 3531c19eea97..762ff928bc87 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -456,9 +456,6 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) netfs_read_to_pagecache(rreq, NULL); - if (sink) - folio_put(sink); - ret = netfs_wait_for_read(rreq); if (ret >= 0) { if (group) @@ -470,6 +467,9 @@ static int netfs_read_gaps(struct file *file, struct folio *folio) flush_dcache_folio(folio); folio_mark_uptodate(folio); } + + if (sink) + folio_put(sink); folio_unlock(folio); netfs_put_request(rreq, netfs_rreq_trace_put_return); return ret < 0 ? ret : 0; From 22ae28aae43623be235ff455558cdd13fbe2daeb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:55 +0100 Subject: [PATCH 244/697] netfs: Fix leak of request in netfs_write_begin() error handling [ Upstream commit 5046a34f0643441f05b0253ea64e1a3af87efe14 ] Fix netfs_write_begin() to not leak our ref on the request in the event that we get an error from netfs_wait_for_read(). Fixes: 4090b31422a6 ("netfs: Add a function to consolidate beginning a read") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-19-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 762ff928bc87..085cd392bf5a 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -686,9 +686,9 @@ int netfs_write_begin(struct netfs_inode *ctx, netfs_read_to_pagecache(rreq, NULL); ret = netfs_wait_for_read(rreq); + netfs_put_request(rreq, netfs_rreq_trace_put_return); if (ret < 0) goto error; - netfs_put_request(rreq, netfs_rreq_trace_put_return); have_folio: ret = folio_wait_private_2_killable(folio); From 6080fa3ecfbb4448a3b47368629534c09b6ec750 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:56 +0100 Subject: [PATCH 245/697] netfs: Fix potential UAF in netfs_unlock_abandoned_read_pages() [ Upstream commit dbe556972100fabb8e5a1b3d2163831ff07b1e8e ] netfs_unlock_abandoned_read_pages(rreq) accesses the index of the folios it is wanting to unlock and compares that to rreq->no_unlock_folio so that it doesn't unlock a folio being read for netfs_perform_write() or netfs_write_begin(). However, given that netfs_unlock_abandoned_read_pages() is called _after_ NETFS_RREQ_IN_PROGRESS is cleared, the one folio that it's not allowed to dereference is the one specified by ->no_unlock_folio as ownership immediately reverts to the caller. Fix this by storing the folio pointer instead and using that rather than the index. Also fix netfs_unlock_read_folio() where the same applies. Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-20-dhowells@redhat.com cc: Paulo Alcantara cc: Viacheslav Dubeyko cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 4 ++-- fs/netfs/read_collect.c | 2 +- fs/netfs/read_retry.c | 2 +- include/linux/netfs.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 085cd392bf5a..ebaabec37632 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -669,7 +669,7 @@ int netfs_write_begin(struct netfs_inode *ctx, ret = PTR_ERR(rreq); goto error; } - rreq->no_unlock_folio = folio->index; + rreq->no_unlock_folio = folio; __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); ret = netfs_begin_cache_read(rreq, ctx); @@ -735,7 +735,7 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, goto error; } - rreq->no_unlock_folio = folio->index; + rreq->no_unlock_folio = folio; __set_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags); ret = netfs_begin_cache_read(rreq, ctx); if (ret == -ENOMEM || ret == -EINTR || ret == -ERESTARTSYS) diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c index d2d902f46627..4c7312a4c859 100644 --- a/fs/netfs/read_collect.c +++ b/fs/netfs/read_collect.c @@ -83,7 +83,7 @@ static void netfs_unlock_read_folio(struct netfs_io_request *rreq, } just_unlock: - if (folio->index == rreq->no_unlock_folio && + if (folio == rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { _debug("no unlock"); } else { diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c index 68fc869513ef..999177426141 100644 --- a/fs/netfs/read_retry.c +++ b/fs/netfs/read_retry.c @@ -288,7 +288,7 @@ void netfs_unlock_abandoned_read_pages(struct netfs_io_request *rreq) struct folio *folio = folioq_folio(p, slot); if (folio && !folioq_is_marked2(p, slot)) { - if (folio->index == rreq->no_unlock_folio && + if (folio == rreq->no_unlock_folio && test_bit(NETFS_RREQ_NO_UNLOCK_FOLIO, &rreq->flags)) { _debug("no unlock"); diff --git a/include/linux/netfs.h b/include/linux/netfs.h index ba17ac5bf356..62a528f90666 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -252,7 +252,7 @@ struct netfs_io_request { unsigned long long collected_to; /* Point we've collected to */ unsigned long long cleaned_to; /* Position we've cleaned folios to */ unsigned long long abandon_to; /* Position to abandon folios to */ - pgoff_t no_unlock_folio; /* Don't unlock this folio after read */ + const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ unsigned int debug_id; unsigned int rsize; /* Maximum read size (0 for none) */ From 3d9601c029b934b5b6a10f99791467b10eb6b211 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:57 +0100 Subject: [PATCH 246/697] netfs: Fix partial invalidation of streaming-write folio [ Upstream commit 6d91acc7fb85d33ea58fca9b964a32a453937f4b ] In netfs_invalidate_folio(), if the region of a partial invalidation overlaps the front (but not all) of a dirty write cached in a streaming write page (dirty, but not uptodate, with the dirty region tracked by a netfs_folio struct), the function modifies the dirty region - but incorrectly as it moves the region forward by setting the start to the start, not the end, of the invalidation region. Fix this by setting finfo->dirty_offset to the end of the invalidation region (iend). Fixes: cce6bfa6ca0e ("netfs: Fix trimming of streaming-write folios in netfs_inval_folio()") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-21-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c index eb309bef7260..1109ac379128 100644 --- a/fs/netfs/misc.c +++ b/fs/netfs/misc.c @@ -255,7 +255,7 @@ void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length) goto erase_completely; /* Move the start of the data. */ finfo->dirty_len = fend - iend; - finfo->dirty_offset = offset; + finfo->dirty_offset = iend; trace_netfs_folio(folio, netfs_folio_trace_invalidate_front); return; } From 551b5c71ee312ca7646ddb605231c1016e8cbb18 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:58 +0100 Subject: [PATCH 247/697] netfs: Fix folio->private handling in netfs_perform_write() [ Upstream commit ccde2ac757c713535b224233a296de40efe5212d ] Under some circumstances, netfs_perform_write() doesn't correctly manipulate folio->private between NULL, NETFS_FOLIO_COPY_TO_CACHE, pointing to a group and pointing to a netfs_folio struct, leading to potential multiple attachments of private data with associated folio ref leaks and also leaks of netfs_folio structs or netfs_group refs. Fix this by consolidating the place at which a folio is marked uptodate in one place and having that look at what's attached to folio->private and decide how to clean it up and then set the new group. Also, the content shouldn't be flushed if group is NULL, even if a group is specified in the netfs_group parameter, as that would be the case for a new folio. A filesystem should always specify netfs_group or never specify netfs_group. The Sashiko auto-review tool noted that it was theoretically possible that the fpos >= ctx->zero_point section might leak if it modified a streaming write folio. This is unlikely, but with a network filesystem, third party changes can happen. It also pointed out that __netfs_set_group() would leak if called multiple times on the same folio from the "whole folio modify section". Fixes: 8f52de0077ba ("netfs: Reduce number of conditional branches in netfs_perform_write()") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-22-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_write.c | 134 +++++++++++++++++++++-------------- include/trace/events/netfs.h | 1 + 2 files changed, 82 insertions(+), 53 deletions(-) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 60215a772357..dd0ce7b769ce 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -13,24 +13,6 @@ #include #include "internal.h" -static void __netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) -{ - if (netfs_group) - folio_attach_private(folio, netfs_get_group(netfs_group)); -} - -static void netfs_set_group(struct folio *folio, struct netfs_group *netfs_group) -{ - void *priv = folio_get_private(folio); - - if (unlikely(priv != netfs_group)) { - if (netfs_group && (!priv || priv == NETFS_FOLIO_COPY_TO_CACHE)) - folio_attach_private(folio, netfs_get_group(netfs_group)); - else if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) - folio_detach_private(folio); - } -} - /* * Grab a folio for writing and lock it. Attempt to allocate as large a folio * as possible to hold as much of the remaining length as possible in one go. @@ -158,6 +140,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, size_t offset; /* Offset into pagecache folio */ size_t part; /* Bytes to write to folio */ size_t copied; /* Bytes copied from user */ + void *priv; offset = pos & (max_chunk - 1); part = min(max_chunk - offset, iov_iter_count(iter)); @@ -203,6 +186,25 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto error_folio_unlock; } + finfo = netfs_folio_info(folio); + group = netfs_folio_group(folio); + + /* If the requested group differs from the group set on the + * page, then we need to flush out the folio if it has a group + * set (ie. is non-NULL). Note that COPY_TO_CACHE is a special + * case, being a netfs annotation rather than an actual group. + * + * The filesystem isn't permitted to mix writes with groups and + * writes without groups as the NULL group is used to indicate + * that no group is set. + */ + if (unlikely(group != netfs_group) && + group != NETFS_FOLIO_COPY_TO_CACHE && + group) { + WARN_ON_ONCE(!netfs_group); + goto flush_content; + } + /* Decide how we should modify a folio. We might be attempting * to do write-streaming, as we don't want to a local RMW cycle * if we can avoid it. If we're doing local caching or content @@ -210,22 +212,14 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, * file is open readably, then we let ->read_folio() fill in * the gaps. */ - finfo = netfs_folio_info(folio); - group = netfs_folio_group(folio); - - if (unlikely(group != netfs_group) && - group != NETFS_FOLIO_COPY_TO_CACHE) - goto flush_content; - if (folio_test_uptodate(folio)) { if (mapping_writably_mapped(mapping)) flush_dcache_folio(folio); copied = copy_folio_from_iter_atomic(folio, offset, part, iter); if (unlikely(copied == 0)) goto copy_failed; - netfs_set_group(folio, netfs_group); trace = netfs_folio_is_uptodate; - goto copied; + goto copied_uptodate; } /* If the page is above the zero-point then we assume that the @@ -238,24 +232,22 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, if (unlikely(copied == 0)) goto copy_failed; folio_zero_segment(folio, offset + copied, flen); - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace = netfs_modify_and_clear; - goto copied; + if (finfo) + trace = netfs_modify_and_clear_rm_finfo; + else + trace = netfs_modify_and_clear; + goto mark_uptodate; } /* See if we can write a whole folio in one go. */ if (!maybe_trouble && offset == 0 && part >= flen) { copied = copy_folio_from_iter_atomic(folio, offset, part, iter); if (likely(copied == part)) { - if (finfo) { + if (finfo) trace = netfs_whole_folio_modify_filled; - goto folio_now_filled; - } - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); - trace = netfs_whole_folio_modify; - goto copied; + else + trace = netfs_whole_folio_modify; + goto mark_uptodate; } if (copied == 0) goto copy_failed; @@ -273,7 +265,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len += finfo->dirty_offset; if (finfo->dirty_len == flen) { trace = netfs_whole_folio_modify_filled_efault; - goto folio_now_filled; + goto mark_uptodate; } if (copied > finfo->dirty_len) finfo->dirty_len = copied; @@ -301,11 +293,11 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, copied = copy_folio_from_iter_atomic(folio, offset, part, iter); if (unlikely(copied == 0)) goto copy_failed; - netfs_set_group(folio, netfs_group); trace = netfs_just_prefetch; - goto copied; + goto copied_uptodate; } + /* Do a streaming write on a folio that has nothing in it yet. */ if (!finfo) { ret = -EIO; if (WARN_ON(folio_get_private(folio))) @@ -314,10 +306,8 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, if (unlikely(copied == 0)) goto copy_failed; if (offset == 0 && copied == flen) { - __netfs_set_group(folio, netfs_group); - folio_mark_uptodate(folio); trace = netfs_streaming_filled_page; - goto copied; + goto mark_uptodate; } finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); @@ -346,7 +336,7 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, finfo->dirty_len += copied; if (finfo->dirty_offset == 0 && finfo->dirty_len == flen) { trace = netfs_streaming_cont_filled_page; - goto folio_now_filled; + goto mark_uptodate; } trace = netfs_streaming_write_cont; goto copied; @@ -362,13 +352,36 @@ ssize_t netfs_perform_write(struct kiocb *iocb, struct iov_iter *iter, goto out; continue; - folio_now_filled: - if (finfo->netfs_group) - folio_change_private(folio, finfo->netfs_group); - else - folio_detach_private(folio); + /* Mark a folio as being up to data when we've filled it + * completely. If the folio has a group attached, then it must + * be the same group, otherwise we should have flushed it out + * above. We have to get rid of the netfs_folio struct if + * there was one. + */ + mark_uptodate: folio_mark_uptodate(folio); - kfree(finfo); + + copied_uptodate: + priv = folio_get_private(folio); + if (likely(priv == netfs_group)) { + /* Already set correctly; no change required. */ + } else if (priv == NETFS_FOLIO_COPY_TO_CACHE) { + if (!netfs_group) + folio_detach_private(folio); + else + folio_change_private(folio, netfs_get_group(netfs_group)); + } else if (!priv) { + folio_attach_private(folio, netfs_get_group(netfs_group)); + } else { + WARN_ON_ONCE(!finfo); + if (netfs_group) + /* finfo->netfs_group has a ref */ + folio_change_private(folio, netfs_group); + else + folio_detach_private(folio); + kfree(finfo); + } + copied: trace_netfs_folio(folio, trace); flush_dcache_folio(folio); @@ -531,6 +544,7 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr struct inode *inode = file_inode(file); struct netfs_inode *ictx = netfs_inode(inode); vm_fault_t ret = VM_FAULT_NOPAGE; + void *priv; int err; _enter("%lx", folio->index); @@ -551,7 +565,9 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr } group = netfs_folio_group(folio); - if (group != netfs_group && group != NETFS_FOLIO_COPY_TO_CACHE) { + if (group && + group != netfs_group && + group != NETFS_FOLIO_COPY_TO_CACHE) { folio_unlock(folio); err = filemap_fdatawrite_range(mapping, folio_pos(folio), @@ -573,7 +589,19 @@ vm_fault_t netfs_page_mkwrite(struct vm_fault *vmf, struct netfs_group *netfs_gr trace_netfs_folio(folio, netfs_folio_trace_mkwrite_plus); else trace_netfs_folio(folio, netfs_folio_trace_mkwrite); - netfs_set_group(folio, netfs_group); + + priv = folio_get_private(folio); + if (priv != netfs_group) { + if (!netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) + folio_detach_private(folio); + else if (netfs_group && priv == NETFS_FOLIO_COPY_TO_CACHE) + folio_change_private(folio, netfs_get_group(netfs_group)); + else if (netfs_group && !priv) + folio_attach_private(folio, netfs_get_group(netfs_group)); + else + WARN_ON_ONCE(1); + } + file_update_time(file); set_bit(NETFS_ICTX_MODIFIED_ATTR, &ictx->flags); if (ictx->ops->post_modify) diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index db045135406c..3fe3980902c2 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -181,6 +181,7 @@ EM(netfs_whole_folio_modify_filled, "mod-whole-f+") \ EM(netfs_whole_folio_modify_filled_efault, "mod-whole-f+!") \ EM(netfs_modify_and_clear, "mod-n-clear") \ + EM(netfs_modify_and_clear_rm_finfo, "mod-n-clear+") \ EM(netfs_streaming_write, "mod-streamw") \ EM(netfs_streaming_write_cont, "mod-streamw+") \ EM(netfs_flush_content, "flush") \ From f17b9121bb99f88188ec9be2db5da1d561f4c01b Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:33:59 +0100 Subject: [PATCH 248/697] netfs: Fix netfs_read_folio() to wait on writeback [ Upstream commit ded0c6f1606061148c202825f7e53d711f9f84cf ] Fix netfs_read_folio() to wait for an ongoing writeback to complete so that it can trust the dirty flag and whatever is attached to folio->private (folio->private may get cleaned up by the collector before it clears the writeback flag). Fixes: ee4cdf7ba857 ("netfs: Speed up buffered reading") Closes: https://sashiko.dev/#/patchset/20260414082004.3756080-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-23-dhowells@redhat.com cc: Paulo Alcantara cc: Matthew Wilcox cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/netfs/buffered_read.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index ebaabec37632..fab3181c7f86 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -502,6 +502,8 @@ int netfs_read_folio(struct file *file, struct folio *folio) struct netfs_inode *ctx = netfs_inode(mapping->host); int ret; + folio_wait_writeback(folio); + if (folio_test_dirty(folio)) return netfs_read_gaps(file, folio); From 77bb293049d61e04c12b24ebbffafaf5ab36af90 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 12 May 2026 13:34:00 +0100 Subject: [PATCH 249/697] netfs, afs: Fix write skipping in dir/link writepages [ Upstream commit 9871938f99cc6cb266a77265491660e2375271f5 ] Fix netfs_write_single() and afs_single_writepages() to better handle a write that would be skipped due to lock contention and WB_SYNC_NONE by returning 1 from netfs_write_single() if it skipped and making afs_single_writepages() skip also. If a skip occurs, the inode must be re-marked as the VFS may have cleared the mark. This is really only theoretical for directories in netfs_write_single() as the only path to that is through afs_single_writepages() that takes the ->validate_lock around it, thereby serialising it. Fixes: 6dd80936618c ("afs: Use netfslib for directories") Signed-off-by: David Howells Link: https://patch.msgid.link/20260512123404.719402-24-dhowells@redhat.com cc: Marc Dionne cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Sasha Levin --- fs/afs/dir.c | 11 ++++++++++- fs/netfs/write_issue.c | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 89d36e3e5c79..fa84610e0b0d 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -2207,7 +2207,14 @@ int afs_single_writepages(struct address_space *mapping, /* Need to lock to prevent the folio queue and folios from being thrown * away. */ - down_read(&dvnode->validate_lock); + if (!down_read_trylock(&dvnode->validate_lock)) { + if (wbc->sync_mode == WB_SYNC_NONE) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&dvnode->netfs.inode); + return 0; + } + down_read(&dvnode->validate_lock); + } if (is_dir ? test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags) : @@ -2215,6 +2222,8 @@ int afs_single_writepages(struct address_space *mapping, iov_iter_folio_queue(&iter, ITER_SOURCE, dvnode->directory, 0, 0, i_size_read(&dvnode->netfs.inode)); ret = netfs_writeback_single(mapping, wbc, &iter); + if (ret == 1) + ret = 0; /* Skipped write due to lock conflict. */ } up_read(&dvnode->validate_lock); diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 9bf05099155d..03d170b9022b 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -829,6 +829,9 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, * * Write a monolithic, non-pagecache object back to the server and/or * the cache. + * + * Return: 0 if successful; 1 if skipped due to lock conflict and WB_SYNC_NONE; + * or a negative error code. */ int netfs_writeback_single(struct address_space *mapping, struct writeback_control *wbc, @@ -845,8 +848,10 @@ int netfs_writeback_single(struct address_space *mapping, if (!mutex_trylock(&ictx->wb_lock)) { if (wbc->sync_mode == WB_SYNC_NONE) { + /* The VFS will have undirtied the inode. */ + netfs_single_mark_inode_dirty(&ictx->inode); netfs_stat(&netfs_n_wb_lock_skip); - return 0; + return 1; } netfs_stat(&netfs_n_wb_lock_wait); mutex_lock(&ictx->wb_lock); From cfd62907f3cdbc3b6da8f49ba907c0390018fe5e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 9 May 2026 00:13:37 +0200 Subject: [PATCH 250/697] net: ethernet: cortina: Make RX SKB per-port [ Upstream commit 06937db21ee311ed07eba47954447245041a982d ] The SKB used to assemble packets from fragments in gmac_rx() is static local, but the Gemini has two ethernet ports, meaning there can be races between the ports on a bad day if a device is using both. Make the RX SKB a per-port variable and carry it over between invocations in the port struct instead. Zero the pointer once we call napi_gro_frags(), on error (after calling napi_free_frags()) or if the port is stopped. Zero it in some place where not strictly necessary just to emphasize what is going on. This was found by Sashiko during normal patch review. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-2-6c5d20ddc35b@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/cortina/gemini.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 6a2004bbe87f..7cf3ed7215a3 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -122,6 +122,8 @@ struct gemini_ethernet_port { struct napi_struct napi; struct hrtimer rx_coalesce_timer; unsigned int rx_coalesce_nsecs; + struct sk_buff *rx_skb; + unsigned int freeq_refill; struct gmac_txq txq[TX_QUEUE_NUM]; unsigned int txq_order; @@ -1443,10 +1445,10 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short m = (1 << port->rxq_order) - 1; struct gemini_ethernet *geth = port->geth; void __iomem *ptr_reg = port->rxq_rwptr; + struct sk_buff *skb = port->rx_skb; unsigned int frame_len, frag_len; struct gmac_rxdesc *rx = NULL; struct gmac_queue_page *gpage; - static struct sk_buff *skb; union gmac_rxdesc_0 word0; union gmac_rxdesc_1 word1; union gmac_rxdesc_3 word3; @@ -1500,6 +1502,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (skb) { napi_free_frags(&port->napi); port->stats.rx_dropped++; + skb = NULL; } skb = gmac_skb_if_good_frame(port, word0, frame_len); @@ -1550,6 +1553,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) port->stats.rx_dropped++; } + port->rx_skb = skb; writew(r, ptr_reg); return budget; } @@ -1877,6 +1881,7 @@ static int gmac_stop(struct net_device *netdev) gmac_disable_tx_rx(netdev); gmac_stop_dma(port); napi_disable(&port->napi); + port->rx_skb = NULL; gmac_enable_irq(netdev, 0); gmac_cleanup_rxq(netdev); From 3cd05250a2dfa9f17035f51a0b23d0b70dfa9209 Mon Sep 17 00:00:00 2001 From: Andreas Haarmann-Thiemann Date: Tue, 5 May 2026 23:52:17 +0200 Subject: [PATCH 251/697] net: ethernet: cortina: Drop half-assembled SKB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit b266bacba796ff5c4dcd2ae2fc08aacf7ab39153 ] In gmac_rx() (drivers/net/ethernet/cortina/gemini.c), when gmac_get_queue_page() returns NULL for the second page of a multi-page fragment, the driver logs an error and continues — but does not free the partially assembled skb that was being assembled via napi_build_skb() / napi_get_frags(). Free the in-progress partially assembled skb via napi_free_frags() and increase the number of dropped frames appropriately and assign the skb pointer NULL to make sure it is not lingering around, matching the pattern already used elsewhere in the driver. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Signed-off-by: Andreas Haarmann-Thiemann Signed-off-by: Linus Walleij Reviewed-by: Alexander Lobakin Link: https://patch.msgid.link/20260505-gemini-ethernet-fix-v2-1-997c31d06079@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/cortina/gemini.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 7cf3ed7215a3..63f444e13d6f 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -1494,6 +1494,11 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) gpage = gmac_get_queue_page(geth, port, mapping + PAGE_SIZE); if (!gpage) { dev_err(geth->dev, "could not find mapping\n"); + if (skb) { + napi_free_frags(&port->napi); + port->stats.rx_dropped++; + skb = NULL; + } continue; } page = gpage->page; From c373b34877afea61c89e0dd2e38948c624249b9b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 9 May 2026 00:13:38 +0200 Subject: [PATCH 252/697] net: ethernet: cortina: Carry over frag counter [ Upstream commit ebd8ec2b309e3a447851b456ccaf8fb39f3661e7 ] The gmac_rx() NAPI poll function assembles packets in an SKB from a ring buffer. If the ring buffer gets completely emptied during a poll cycle, we exit gmac_rx(), but the packet is not yet completely assembled in the SKB, yet the fragment counter frag_nr is reset to zero on the next invocation. Solve this by making the RX fragment counter a part of the port struct, and carry it over between invocations. Reset the fragment counter only right after calling napi_gro_frags(), on error (after calling napi_free_frags()) or if stopping the port. Reset it in some place where not strictly necessary just to emphasize what is going on. This was found by Sashiko during normal patch review. Fixes: 4d5ae32f5e1e ("net: ethernet: Add a driver for Gemini gigabit ethernet") Link: https://sashiko.dev/#/patchset/20260505-gemini-ethernet-fix-v2-1-997c31d06079%40kernel.org Signed-off-by: Linus Walleij Link: https://patch.msgid.link/20260509-gemini-ethernet-fixes-v1-3-6c5d20ddc35b@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/cortina/gemini.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 63f444e13d6f..40bec34f06a7 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -123,6 +123,7 @@ struct gemini_ethernet_port { struct hrtimer rx_coalesce_timer; unsigned int rx_coalesce_nsecs; struct sk_buff *rx_skb; + unsigned int rx_frag_nr; unsigned int freeq_refill; struct gmac_txq txq[TX_QUEUE_NUM]; @@ -1445,6 +1446,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short m = (1 << port->rxq_order) - 1; struct gemini_ethernet *geth = port->geth; void __iomem *ptr_reg = port->rxq_rwptr; + unsigned int frag_nr = port->rx_frag_nr; struct sk_buff *skb = port->rx_skb; unsigned int frame_len, frag_len; struct gmac_rxdesc *rx = NULL; @@ -1458,7 +1460,6 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) unsigned short r, w; union dma_rwptr rw; dma_addr_t mapping; - int frag_nr = 0; spin_lock_irqsave(&geth->irq_lock, flags); rw.bits32 = readl(ptr_reg); @@ -1498,6 +1499,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) napi_free_frags(&port->napi); port->stats.rx_dropped++; skb = NULL; + frag_nr = 0; } continue; } @@ -1508,6 +1510,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) napi_free_frags(&port->napi); port->stats.rx_dropped++; skb = NULL; + frag_nr = 0; } skb = gmac_skb_if_good_frame(port, word0, frame_len); @@ -1542,6 +1545,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (word3.bits32 & EOF_BIT) { napi_gro_frags(&port->napi); skb = NULL; + frag_nr = 0; --budget; } continue; @@ -1550,6 +1554,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) if (skb) { napi_free_frags(&port->napi); skb = NULL; + frag_nr = 0; } if (mapping) @@ -1559,6 +1564,7 @@ static unsigned int gmac_rx(struct net_device *netdev, unsigned int budget) } port->rx_skb = skb; + port->rx_frag_nr = frag_nr; writew(r, ptr_reg); return budget; } @@ -1887,6 +1893,7 @@ static int gmac_stop(struct net_device *netdev) gmac_stop_dma(port); napi_disable(&port->napi); port->rx_skb = NULL; + port->rx_frag_nr = 0; gmac_enable_irq(netdev, 0); gmac_cleanup_rxq(netdev); From 9bc70fe995da15dab1de93174f8a7e6db9b55c88 Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 8 May 2026 19:37:28 -0700 Subject: [PATCH 253/697] net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference [ Upstream commit 36a8d04a8293afcb9304cf0cd3741f67698f2a1a ] The legacy ARM board file for MACH_MX31ADS was removed in commit c93197b0041d ("ARM: imx: Remove i.MX31 board files"), but a reference to it remained in the cs89x0 driver. Drop this unused code. Signed-off-by: Ethan Nelson-Moore Fixes: c93197b0041d ("ARM: imx: Remove i.MX31 board files") Link: https://patch.msgid.link/20260509023732.42256-1-enelsonmoore@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/cirrus/cs89x0.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c index fa5857923db4..b4bfd6c174e7 100644 --- a/drivers/net/ethernet/cirrus/cs89x0.c +++ b/drivers/net/ethernet/cirrus/cs89x0.c @@ -1271,7 +1271,6 @@ static const struct net_device_ops net_ops = { static void __init reset_chip(struct net_device *dev) { -#if !defined(CONFIG_MACH_MX31ADS) struct net_local *lp = netdev_priv(dev); unsigned long reset_start_time; @@ -1298,7 +1297,6 @@ static void __init reset_chip(struct net_device *dev) while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 && time_before(jiffies, reset_start_time + 2)) ; -#endif /* !CONFIG_MACH_MX31ADS */ } /* This is the real probe routine. From acde4692afcdaea6de3e2996ddfaeaa7ae6b0130 Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Wed, 6 May 2026 15:42:38 +0200 Subject: [PATCH 254/697] wifi: ath11k: fix error path leaks in some WMI WOW calls [ Upstream commit 55dda532bbc261aef495e403c8900c5e2ab5fa34 ] Fix two instances where we used to directly return the result of ath11k_wmi_cmd_send(...). Because we did not check the return value, we also did not free the skb in the error path. Fixes: 79802b13a492 ("ath11k: implement WoW enable and wakeup commands") Signed-off-by: Nicolas Escande Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260506134240.2284016-2-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/wmi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 110035dae8a6..e1b00dc811e7 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -9191,6 +9191,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) struct wmi_wow_host_wakeup_ind *cmd; struct sk_buff *skb; size_t len; + int ret; len = sizeof(*cmd); skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -9204,14 +9205,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n"); - return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath11k_wmi_wow_enable(struct ath11k *ar) { struct wmi_wow_enable_cmd *cmd; struct sk_buff *skb; - int len; + int ret, len; len = sizeof(*cmd); skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); @@ -9226,7 +9233,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar) cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED; ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n"); - return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n"); + dev_kfree_skb(skb); + } + + return ret; } int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, From d94127d04017f48408a1d0378b173f4ce4d7f72b Mon Sep 17 00:00:00 2001 From: Nicolas Escande Date: Wed, 6 May 2026 15:42:40 +0200 Subject: [PATCH 255/697] wifi: ath11k: fix error path leak in ath11k_tm_cmd_wmi_ftm() [ Upstream commit 7320d6eb861e9913193a7801834c661381756a79 ] This is similar to what was fixed by previous patches. We have a call to ath11k_wmi_cmd_send() which does check the return value, but forgot to free the related skb on error. Fixes: b43310e44edc ("wifi: ath11k: factory test mode support") Signed-off-by: Nicolas Escande Reviewed-by: Baochen Qiang Reviewed-by: Rameshkumar Sundaram Link: https://patch.msgid.link/20260506134240.2284016-4-nico.escande@gmail.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/testmode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index a9751ea2a0b7..c72eed358f6d 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); if (ret) { ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret); + dev_kfree_skb(skb); goto out; } From e1b429d8e712e656d25915548f621244e9fb2223 Mon Sep 17 00:00:00 2001 From: Kang Yang Date: Tue, 28 Apr 2026 14:17:37 +0800 Subject: [PATCH 256/697] wifi: ath10k: skip WMI and beacon transmission when device is wedged [ Upstream commit 54a5b38e4396530e5b2f12b54d3844e860ab6784 ] In ath10k_wmi_cmd_send(), the current code detects ATH10K_STATE_WEDGED and sets ret to -ESHUTDOWN, but still proceeds to transmit pending beacons and calls ath10k_wmi_cmd_send_nowait(). This can lead to incorrect behavior, as WMI commands and beacons are still sent after the device has been marked as wedged, and the original -ESHUTDOWN return value may be overwritten by the result of the send path. The wedged state indicates the hardware is already unreliable, and no further interaction with firmware is expected or meaningful in this state. Fix this by skipping beacon transmission and the WMI send path entirely once ATH10K_STATE_WEDGED is detected, ensuring consistent return values and avoiding unnecessary firmware interaction. Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00288-QCARMSWPZ-1 Tested-on: QCA6174 hw3.2 SDIO WLAN.RMH.4.4.1-00189 Fixes: c256a94d1b1b ("wifi: ath10k: shutdown driver when hardware is unreliable") Signed-off-by: Kang Yang Reviewed-by: Rameshkumar Sundaram Reviewed-by: Baochen Qiang Link: https://patch.msgid.link/20260428061737.37-1-kang.yang@oss.qualcomm.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath10k/wmi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ce22141e5efd..cd20508399b9 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -3,7 +3,6 @@ * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ @@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) ret = -ESHUTDOWN; ath10k_dbg(ar, ATH10K_DBG_WMI, "drop wmi command %d, hardware is wedged\n", cmd_id); - } - /* try to send pending beacons first. they take priority */ - ath10k_wmi_tx_beacons_nowait(ar); + } else { + /* try to send pending beacons first. they take priority */ + ath10k_wmi_tx_beacons_nowait(ar); - ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); - - if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) - ret = -ESHUTDOWN; + ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) + ret = -ESHUTDOWN; + } (ret != -EAGAIN); }), 3 * HZ); From d947e6685ff4bb379dd7c5c14aaf511c689f561f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:28:55 -0700 Subject: [PATCH 257/697] net: shaper: flip the polarity of the valid flag [ Upstream commit 7cee43fcb0c3f71441d2faaa8c2202b6a88b6bef ] The usual way of inserting entries which are not yet fully ready into XArray is to have a VALID flag. The shaper code has a NOT_VALID flag. Since XArray code does not let us create entries with marks already set - the creation of entries is currently not atomic. Flip the polarity of the VALID flag. This closes the tiny race in net_shaper_pre_insert() of entries being created without the NOT_VALID flag. Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-2-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index e41a82241230..64c9d6cf6756 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -275,11 +275,13 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, parent->id = 0; } -/* - * MARK_0 is already in use due to XA_FLAGS_ALLOC, can't reuse such flag as - * it's cleared by xa_store(). +/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on + * an entry only after the device-side configuration has completed + * successfully (see net_shaper_commit()). Lookups and dumps must filter on + * this mark to avoid exposing tentative entries inserted by + * net_shaper_pre_insert() while the driver call is still in flight. */ -#define NET_SHAPER_NOT_VALID XA_MARK_1 +#define NET_SHAPER_VALID XA_MARK_1 static struct net_shaper * net_shaper_lookup(struct net_shaper_binding *binding, @@ -289,8 +291,8 @@ net_shaper_lookup(struct net_shaper_binding *binding, struct net_shaper_hierarchy *hierarchy; hierarchy = net_shaper_hierarchy_rcu(binding); - if (!hierarchy || xa_get_mark(&hierarchy->shapers, index, - NET_SHAPER_NOT_VALID)) + if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, + NET_SHAPER_VALID)) return NULL; return xa_load(&hierarchy->shapers, index); @@ -370,13 +372,10 @@ static int net_shaper_pre_insert(struct net_shaper_binding *binding, goto free_id; } - /* Mark 'tentative' shaper inside the hierarchy container. - * xa_set_mark is a no-op if the previous store fails. + /* Insert as 'tentative' (no VALID mark). The mark will be set by + * net_shaper_commit() once the driver-side configuration succeeds. */ - xa_lock(&hierarchy->shapers); - prev = __xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); - __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_NOT_VALID); - xa_unlock(&hierarchy->shapers); + prev = xa_store(&hierarchy->shapers, index, cur, GFP_KERNEL); if (xa_err(prev)) { NL_SET_ERR_MSG(extack, "Can't insert shaper into device store"); kfree_rcu(cur, rcu); @@ -413,8 +412,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, /* Successful update: drop the tentative mark * and update the hierarchy container. */ - __xa_clear_mark(&hierarchy->shapers, index, - NET_SHAPER_NOT_VALID); + __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); *cur = shapers[i]; } xa_unlock(&hierarchy->shapers); @@ -431,8 +429,9 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) return; xa_lock(&hierarchy->shapers); - xa_for_each_marked(&hierarchy->shapers, index, cur, - NET_SHAPER_NOT_VALID) { + xa_for_each(&hierarchy->shapers, index, cur) { + if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) + continue; __xa_erase(&hierarchy->shapers, index); kfree(cur); } @@ -836,7 +835,8 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, goto out_unlock; for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, - U32_MAX, XA_PRESENT)); ctx->start_index++) { + U32_MAX, NET_SHAPER_VALID)); + ctx->start_index++) { ret = net_shaper_fill_one(skb, binding, shaper, info); if (ret) break; From d6128451c5918dbdd6777425e701fbf8bc5c07aa Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:28:56 -0700 Subject: [PATCH 258/697] net: shaper: fix trivial ordering issue in net_shaper_commit() [ Upstream commit 235fb5376139c3419f2218349f1fa2f06f24f7ad ] We should update the entry before we mark it as valid. Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-3-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 64c9d6cf6756..92ca3b4c7925 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -295,6 +295,10 @@ net_shaper_lookup(struct net_shaper_binding *binding, NET_SHAPER_VALID)) return NULL; + /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is + * valid, its contents must be visible too. + */ + smp_rmb(); return xa_load(&hierarchy->shapers, index); } @@ -412,8 +416,9 @@ static void net_shaper_commit(struct net_shaper_binding *binding, /* Successful update: drop the tentative mark * and update the hierarchy container. */ - __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); *cur = shapers[i]; + smp_wmb(); + __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); } xa_unlock(&hierarchy->shapers); } @@ -837,6 +842,10 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, U32_MAX, NET_SHAPER_VALID)); ctx->start_index++) { + /* Pairs with smp_wmb() in net_shaper_commit(): the entry + * is marked VALID, so its contents must be visible too. + */ + smp_rmb(); ret = net_shaper_fill_one(skb, binding, shaper, info); if (ret) break; From 5098b223f0f0c5c18a3884a8b0ea5bd4a0c7bd75 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:28:57 -0700 Subject: [PATCH 259/697] net: shaper: reject duplicate leaves in GROUP request [ Upstream commit a9a2fa1da619f276580b0d4c5d12efac89e8642b ] net_shaper_nl_group_doit() does not deduplicate NET_SHAPER_A_LEAVES entries. When userspace supplies the same leaf handle twice, the same old-parent pointer lands twice in old_nodes[]. The cleanup loop double frees the parent. Of course the same parent may still be in old_nodes[] twice if we are moving multiple of its leaves. Note that this patch also implicitly fixes the fact that the i >= leaves_count path forgets to set ret. Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-4-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 60 +++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 92ca3b4c7925..ae19af13c224 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -941,6 +941,46 @@ static int net_shaper_handle_cmp(const struct net_shaper_handle *a, return memcmp(a, b, sizeof(*a)); } +static int net_shaper_parse_leaves(struct net_shaper_binding *binding, + struct genl_info *info, + const struct net_shaper *node, + struct net_shaper *leaves, + int leaves_count) +{ + struct nlattr *attr; + int i, j, ret, rem; + + i = 0; + nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, + genlmsg_data(info->genlhdr), + genlmsg_len(info->genlhdr), rem) { + if (WARN_ON_ONCE(i >= leaves_count)) + return -EINVAL; + + ret = net_shaper_parse_leaf(binding, attr, info, + node, &leaves[i]); + if (ret) + return ret; + + /* Reject duplicates */ + for (j = 0; j < i; j++) { + if (net_shaper_handle_cmp(&leaves[i].handle, + &leaves[j].handle)) + continue; + + NL_SET_ERR_MSG_ATTR_FMT(info->extack, attr, + "Duplicate leaf shaper %d:%d", + leaves[i].handle.scope, + leaves[i].handle.id); + return -EINVAL; + } + + i++; + } + + return 0; +} + static int net_shaper_parent_from_leaves(int leaves_count, const struct net_shaper *leaves, struct net_shaper *node, @@ -1198,10 +1238,9 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) struct net_shaper **old_nodes, *leaves, node = {}; struct net_shaper_hierarchy *hierarchy; struct net_shaper_binding *binding; - int i, ret, rem, leaves_count; + int i, ret, leaves_count; int old_nodes_count = 0; struct sk_buff *msg; - struct nlattr *attr; if (GENL_REQ_ATTR_CHECK(info, NET_SHAPER_A_LEAVES)) return -EINVAL; @@ -1229,19 +1268,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) if (ret) goto free_leaves; - i = 0; - nla_for_each_attr_type(attr, NET_SHAPER_A_LEAVES, - genlmsg_data(info->genlhdr), - genlmsg_len(info->genlhdr), rem) { - if (WARN_ON_ONCE(i >= leaves_count)) - goto free_leaves; - - ret = net_shaper_parse_leaf(binding, attr, info, - &node, &leaves[i]); - if (ret) - goto free_leaves; - i++; - } + ret = net_shaper_parse_leaves(binding, info, &node, + leaves, leaves_count); + if (ret) + goto free_leaves; /* Prepare the msg reply in advance, to avoid device operation * rollback on allocation failure. From f817ce8d1943841949a322408bf9b7ec31368ad5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:28:59 -0700 Subject: [PATCH 260/697] net: shaper: set ret to -ENOMEM when genlmsg_new() fails in group_doit [ Upstream commit 8054f85b83f42a37d482fc77ea7c9ff06a9407d9 ] genlmsg_new() alloc failure path in net_shaper_nl_group_doit() forgets to set ret before jumping to error handling. Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-6-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index ae19af13c224..ee0d1f061343 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -1277,8 +1277,10 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) * rollback on allocation failure. */ msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); - if (!msg) + if (!msg) { + ret = -ENOMEM; goto free_leaves; + } hierarchy = net_shaper_hierarchy_setup(binding); if (!hierarchy) { From d7c2bbbaa2c428fbcf4697ff949de5426ae42d12 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:29:00 -0700 Subject: [PATCH 261/697] net: shaper: fix undersized reply skb allocation in GROUP command [ Upstream commit 0f9a857e34d0f8c018a3e4435c6f0e92e8d2f38c ] net_shaper_group_send_reply() writes both the NET_SHAPER_A_IFINDEX attribute (via net_shaper_fill_binding()) and the nested NET_SHAPER_A_HANDLE attribute (via net_shaper_fill_handle()), but the reply skb at the call site in net_shaper_nl_group_doit() is allocated using net_shaper_handle_size(), which only accounts for the nested handle. The allocation is therefore short by nla_total_size(sizeof(u32)) (8 bytes) for the IFINDEX attribute. In practice the slab allocator rounds up the small allocation so the bug is latent, but the size accounting is wrong and could bite if the reply grew further. Introduce net_shaper_group_reply_size() that accounts for the full reply payload and use it both at the genlmsg_new() call site and in the defensive WARN_ONCE message. Fixes: 5d5d4700e75d ("net-shapers: implement NL group operation") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-7-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index ee0d1f061343..5338842122a2 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -90,6 +90,12 @@ static int net_shaper_handle_size(void) nla_total_size(sizeof(u32))); } +static int net_shaper_group_reply_size(void) +{ + return nla_total_size(sizeof(u32)) + /* NET_SHAPER_A_IFINDEX */ + net_shaper_handle_size(); /* NET_SHAPER_A_HANDLE */ +} + static int net_shaper_fill_binding(struct sk_buff *msg, const struct net_shaper_binding *binding, u32 type) @@ -1228,7 +1234,7 @@ static int net_shaper_group_send_reply(struct net_shaper_binding *binding, free_msg: /* Should never happen as msg is pre-allocated with enough space. */ WARN_ONCE(true, "calculated message payload length (%d)", - net_shaper_handle_size()); + net_shaper_group_reply_size()); nlmsg_free(msg); return -EMSGSIZE; } @@ -1276,7 +1282,7 @@ int net_shaper_nl_group_doit(struct sk_buff *skb, struct genl_info *info) /* Prepare the msg reply in advance, to avoid device operation * rollback on allocation failure. */ - msg = genlmsg_new(net_shaper_handle_size(), GFP_KERNEL); + msg = genlmsg_new(net_shaper_group_reply_size(), GFP_KERNEL); if (!msg) { ret = -ENOMEM; goto free_leaves; From 77ec90d41c5955b88f5c5d9ee21d0650b9fd7c12 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:29:03 -0700 Subject: [PATCH 262/697] net: shaper: enforce singleton NETDEV scope with id 0 [ Upstream commit b62b29e6de6711f5918940aa6ff2bbab6d6af502 ] The NETDEV scope represents a singleton root shaper in the per-device hierarchy. All code assumes NETDEV shapers have id 0: net_shaper_default_parent() hardcodes parent->id = 0 when returning the NETDEV parent for QUEUE/NODE children, and the UAPI documentation describes NETDEV scope as "the main shaper" (singular, not plural). Make sure we reject non-0 IDs. Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-10-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 5338842122a2..5cd244b8109a 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -480,6 +480,12 @@ static int net_shaper_parse_handle(const struct nlattr *attr, else if (handle->scope == NET_SHAPER_SCOPE_NODE) id = NET_SHAPER_ID_UNSPEC; + if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { + NL_SET_ERR_MSG_ATTR(info->extack, id_attr, + "Netdev scope is a singleton, must use ID 0"); + return -EINVAL; + } + handle->id = id; return 0; } From a524863944933e883ff3743d0607e84abd62f377 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:29:04 -0700 Subject: [PATCH 263/697] net: shaper: reject QUEUE scope handle with missing id [ Upstream commit ce372e869f9f492f3d5aa9a0ae75ed52c61d2d6f ] net_shaper_parse_handle() does not enforce that the user provides the handle ID. For NODE the ID defaults to UNSPEC for all other cases it defaults to 0. For NETDEV 0 is the only option. For QUEUE defaulting to 0 makes less intuitive sense. Specifically because the behavior should (IMHO) be the same for all cases where there may be more than one ID (QUEUE and NODE). We should either document this as intentional or reject. I picked the latter with no strong conviction. Fixes: 4b623f9f0f59 ("net-shapers: implement NL get operation") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-11-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index 5cd244b8109a..fa8520025282 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -475,10 +475,15 @@ static int net_shaper_parse_handle(const struct nlattr *attr, * shaper (any other value). */ id_attr = tb[NET_SHAPER_A_HANDLE_ID]; - if (id_attr) + if (id_attr) { id = nla_get_u32(id_attr); - else if (handle->scope == NET_SHAPER_SCOPE_NODE) + } else if (handle->scope == NET_SHAPER_SCOPE_NODE) { id = NET_SHAPER_ID_UNSPEC; + } else if (handle->scope == NET_SHAPER_SCOPE_QUEUE) { + NL_SET_ERR_ATTR_MISS(info->extack, attr, + NET_SHAPER_A_HANDLE_ID); + return -EINVAL; + } if (id && handle->scope == NET_SHAPER_SCOPE_NETDEV) { NL_SET_ERR_MSG_ATTR(info->extack, id_attr, From 0d48654af4d1390c888389206cc13b51b82c30e6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 11 May 2026 22:51:51 +0100 Subject: [PATCH 264/697] block: don't overwrite bip_vcnt in bio_integrity_copy_user() [ Upstream commit 637ad3a56a3b889527d1dacea6fea2a8bd648140 ] bio_integrity_add_page() already sets bip_vcnt to 1 for the bounce segment. Overwriting it with nr_vecs breaks bip_vcnt <= bip_max_vcnt on WRITE (bip_max_vcnt is 1), so the gap-merge checks in block/blk.h read past the bip_vec[] flex array. On READ the read is in bounds but lands on a saved user bvec instead of the bounce. The line was added for split propagation, but bio_integrity_clone() doesn't copy bip_vcnt and BIP_CLONE_FLAGS excludes BIP_COPY_USER. Fixes: 3991657ae707 ("block: set bip_vcnt correctly") Signed-off-by: David Carlier Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260511215151.346228-1-devnexen@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/bio-integrity.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 12d887349c26..ab4a15437925 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -205,7 +205,6 @@ static int bio_integrity_copy_user(struct bio *bio, struct bio_vec *bvec, } bip->bip_flags |= BIP_COPY_USER; - bip->bip_vcnt = nr_vecs; return 0; free_bip: bio_integrity_free(bio); From 0943f81e1b3176f27dbaf6db268fc69d8a94f0ba Mon Sep 17 00:00:00 2001 From: Casey Chen Date: Mon, 11 May 2026 15:22:30 -0600 Subject: [PATCH 265/697] block: recompute nr_integrity_segments in blk_insert_cloned_request [ Upstream commit 2c6e6a18a37b905cb584eb0dda3ae482162a81ca ] blk_insert_cloned_request() already recomputes nr_phys_segments against the bottom queue, because "the queue settings related to segment counting may differ from the original queue." The exact same reasoning applies to integrity segments: a stacked driver's underlying queue can have tighter virt_boundary_mask, seg_boundary_mask, or max_segment_size than the top queue, in which case blk_rq_count_integrity_sg() against the bottom queue produces a different count than the cached rq->nr_integrity_segments inherited from the source request by blk_rq_prep_clone(). When the cached count is lower than the bottom queue's actual count, blk_rq_map_integrity_sg() trips BUG_ON(segments > rq->nr_integrity_segments); on dispatch. The same families of stacked setups that motivated the existing nr_phys_segments recompute -- dm-multipath fanning out to nvme-rdma in particular -- can produce this. Mirror the nr_phys_segments handling: when the request carries integrity, recompute nr_integrity_segments against the bottom queue and reject the request if it exceeds the bottom queue's max_integrity_segments. blk_rq_count_integrity_sg() and queue_max_integrity_segments() are both already available via , which blk-mq.c includes. This closes a latent gap in the stacking contract and brings the integrity-segment accounting in line with the existing phys-segment accounting. Fixes: 76c313f658d2 ("blk-integrity: improved sg segment mapping") Signed-off-by: Casey Chen Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260511212230.27511-1-cachen@purestorage.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/blk-mq.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/block/blk-mq.c b/block/blk-mq.c index 4ebb92014eae..ab05c5c9e6ae 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -3285,6 +3285,25 @@ blk_status_t blk_insert_cloned_request(struct request *rq) return BLK_STS_IOERR; } + /* + * Integrity segment counting depends on the same queue limits + * (virt_boundary_mask, seg_boundary_mask, max_segment_size) that + * vary across stacked queues, so recompute against the bottom + * queue just like nr_phys_segments above. + */ + if (blk_integrity_rq(rq) && rq->bio) { + unsigned short max_int_segs = queue_max_integrity_segments(q); + + rq->nr_integrity_segments = + blk_rq_count_integrity_sg(rq->q, rq->bio); + if (rq->nr_integrity_segments > max_int_segs) { + printk(KERN_ERR "%s: over max integrity segments limit. (%u > %u)\n", + __func__, rq->nr_integrity_segments, + max_int_segs); + return BLK_STS_IOERR; + } + } + if (q->disk && should_fail_request(q->disk->part0, blk_rq_bytes(rq))) return BLK_STS_IOERR; From 086695145000cc518b69811164ce4aa8e45211b1 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Thu, 5 Feb 2026 09:11:31 +0100 Subject: [PATCH 266/697] HID: quirks: really enable the intended work around for appledisplay [ Upstream commit 5f90dcfa8dc32a488581b78e575cdd7808ba5c78 ] Commit c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") intends to add a quirk for kernels built with Apple Cinema Display support, but it refers to the non-existing config option CONFIG_APPLEDISPLAY, whereas the config option for Apple Cinema Display support is named CONFIG_USB_APPLEDISPLAY. Refer to the intended config option CONFIG_USB_APPLEDISPLAY in the ifdef directive. Fixes: c7fabe4ad921 ("HID: quirks: work around VID/PID conflict for appledisplay") Signed-off-by: Lukas Bulwahn Signed-off-by: Jiri Kosina Signed-off-by: Sasha Levin --- drivers/hid/hid-quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index f6be3ffee023..04d3ec360c1d 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -234,7 +234,7 @@ static const struct hid_device_id hid_quirks[] = { * used as a driver. See hid_scan_report(). */ static const struct hid_device_id hid_have_special_driver[] = { -#if IS_ENABLED(CONFIG_APPLEDISPLAY) +#if IS_ENABLED(CONFIG_USB_APPLEDISPLAY) { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9218) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x9219) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, 0x921c) }, From 76410790f1491c8e06a451045ae223a61c652455 Mon Sep 17 00:00:00 2001 From: Sungwoo Kim Date: Tue, 12 May 2026 01:09:29 -0400 Subject: [PATCH 267/697] block: bio-integrity: Fix null-ptr-deref in bio_integrity_map_user() [ Upstream commit 8582792cf23b3d94674d4d838f7cde9a28d0fcaf ] pin_user_pages_fast() can partially succeed and return the number of pages that were actually pinned. However, the bio_integrity_map_user() does not handle this partial pinning. This leads to a general protection fault since bvec_from_pages() dereferences an unpinned page address, which is 0. To fix this, add a check to verify that all requested memory is pinned. If partial pinning occurs, unpin the memory and return -EFAULT. Kernel Oops: Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN NOPTI KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f] CPU: 0 UID: 0 PID: 1061 Comm: nvme-passthroug Not tainted 7.0.0-11783-g90957f9314e8-dirty #16 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.17.0-0-gb52ca86e094d-prebuilt.qemu.org 04/01/2014 RIP: 0010:bio_integrity_map_user.cold+0x1b0/0x9d6 Fixes: 492c5d455969 ("block: bio-integrity: directly map user buffers") Acked-by: Chao Shi Acked-by: Weidong Zhu Acked-by: Dave Tian Signed-off-by: Sungwoo Kim Tested-by: Shin'ichiro Kawasaki Link: https://github.com/linux-blktests/blktests/pull/244 Link: https://patch.msgid.link/20260512050929.541397-2-iam@sung-woo.kim Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- block/bio-integrity.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/block/bio-integrity.c b/block/bio-integrity.c index ab4a15437925..46c59ed92bd1 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -299,6 +299,24 @@ int bio_integrity_map_user(struct bio *bio, struct iov_iter *iter) if (unlikely(ret < 0)) goto free_bvec; + /* + * Handle partial pinning. This can happen when pin_user_pages_fast() + * returns fewer pages than requested. + */ + if (user_backed_iter(iter) && unlikely(ret != bytes)) { + if (ret > 0) { + int npinned = DIV_ROUND_UP(offset + ret, PAGE_SIZE); + int i; + + for (i = 0; i < npinned; i++) + unpin_user_page(pages[i]); + } + if (pages != stack_pages) + kvfree(pages); + ret = -EFAULT; + goto free_bvec; + } + nr_bvecs = bvec_from_pages(bvec, pages, nr_vecs, bytes, offset, &is_p2p); if (pages != stack_pages) From 97a8e89cdef36207a8776edc03d6931763a06ad0 Mon Sep 17 00:00:00 2001 From: Zack McKevitt Date: Thu, 30 Apr 2026 12:39:01 -0700 Subject: [PATCH 268/697] accel/qaic: Add overflow check to remap_pfn_range during mmap [ Upstream commit aa16b2bc0f02709919e2435f531406531e5bcc69 ] The call to remap_pfn_range in qaic_gem_object_mmap is susceptible to (re)mapping beyond the VMA if the BO is too large. This can cause use after free issues when munmap() unmaps only the VMA region and not the additional mappings. To prevent this, check the remaining size of the VMA before remapping and truncate the remapped length if sg->length is too large. Reported-by: Lukas Maar Fixes: ff13be830333 ("accel/qaic: Add datapath") Reviewed-by: Karol Wachowski Signed-off-by: Zack McKevitt Reviewed-by: Jeff Hugo [jhugo: fix braces from checkpatch --strict] Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260430193858.1178641-1-zachary.mckevitt@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/accel/qaic/qaic_data.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index c4f117edb266..3335c92b0d7d 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -605,8 +605,11 @@ static const struct vm_operations_struct drm_vm_ops = { static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { struct qaic_bo *bo = to_qaic_bo(obj); + unsigned long remap_start; unsigned long offset = 0; + unsigned long remap_end; struct scatterlist *sg; + unsigned long length; int ret = 0; if (drm_gem_is_imported(obj)) @@ -614,11 +617,27 @@ static int qaic_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc for (sg = bo->sgt->sgl; sg; sg = sg_next(sg)) { if (sg_page(sg)) { + /* if sg is too large for the VMA, so truncate it to fit */ + if (check_add_overflow(vma->vm_start, offset, &remap_start)) + return -EINVAL; + if (check_add_overflow(remap_start, sg->length, &remap_end)) + return -EINVAL; + + if (remap_end > vma->vm_end) { + if (check_sub_overflow(vma->vm_end, remap_start, &length)) + return -EINVAL; + } else { + length = sg->length; + } + + if (length == 0) + goto out; + ret = remap_pfn_range(vma, vma->vm_start + offset, page_to_pfn(sg_page(sg)), - sg->length, vma->vm_page_prot); + length, vma->vm_page_prot); if (ret) goto out; - offset += sg->length; + offset += length; } } From d2ea0b8aef8746e147602eac87ca8538f4bc7e66 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 10 May 2026 15:26:40 -0700 Subject: [PATCH 269/697] net/smc: avoid NULL deref of conn->lnk in smc_msg_event tracepoint [ Upstream commit 7bf563badd37cb796df5477d2b78bb64148a1268 ] The smc_msg_event tracepoint class, shared by smc_tx_sendmsg and smc_rx_recvmsg, unconditionally dereferences smc->conn.lnk: __string(name, smc->conn.lnk->ibname) conn->lnk is only set for SMC-R; for SMC-D it is NULL. Other code on these paths already handles this (e.g. !conn->lnk in SMC_STAT_RMB_TX_SIZE_SMALL()). With the tracepoint enabled, the first sendmsg()/recvmsg() on an SMC-D socket crashes: Oops: general protection fault, probably for non-canonical address KASAN: null-ptr-deref in range [...] RIP: 0010:strlen+0x1e/0xa0 Call Trace: trace_event_raw_event_smc_msg_event (net/smc/smc_tracepoint.h:44) smc_rx_recvmsg (net/smc/smc_rx.c:515) smc_recvmsg (net/smc/af_smc.c:2859) __sys_recvfrom (net/socket.c:2315) __x64_sys_recvfrom (net/socket.c:2326) do_syscall_64 The faulting address 0x3e0 is offsetof(struct smc_link, ibname), confirming the NULL ->lnk deref. Enabling the tracepoint requires root, but the trigger itself is unprivileged: socket(AF_SMC, ...) has no capability check, and SMC-D negotiation needs no admin step on s390 or on x86 with the loopback ISM device loaded. Log an empty device name for SMC-D instead of dereferencing NULL. Fixes: aff3083f10bf ("net/smc: Introduce tracepoints for tx and rx msg") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: Dust Li Reviewed-by: Sidraya Jayagond Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/smc/smc_tracepoint.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/smc/smc_tracepoint.h b/net/smc/smc_tracepoint.h index a9a6e3c1113a..53da84f57fd6 100644 --- a/net/smc/smc_tracepoint.h +++ b/net/smc/smc_tracepoint.h @@ -51,7 +51,7 @@ DECLARE_EVENT_CLASS(smc_msg_event, __field(const void *, smc) __field(u64, net_cookie) __field(size_t, len) - __string(name, smc->conn.lnk->ibname) + __string(name, smc->conn.lnk ? smc->conn.lnk->ibname : "") ), TP_fast_assign( From d235f8f7b2644934a6aeac6e3e0fdc1108b3ac08 Mon Sep 17 00:00:00 2001 From: Chenguang Zhao Date: Mon, 11 May 2026 09:43:43 +0800 Subject: [PATCH 270/697] ethtool: fix ethnl_bitmap32_not_zero() bit interval semantics [ Upstream commit 3d042592ebd4c7e44974d556de0b727cb7db4dab ] ethnl_bitmap32_not_zero() should return true if some bit in [start, end) is set: - Fix inverted memchr_inv() sense: return true when the scan finds a non-zero byte, not when the middle words are all zero. - Return false for an empty interval (end <= start). - When end is 32-bit aligned, indices in [start, end) do not include any bits from map[end_word]; return false after earlier checks found no non-zero data. Fixes: 10b518d4e6dd ("ethtool: netlink bitset handling") Signed-off-by: Chenguang Zhao Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/bitset.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ethtool/bitset.c b/net/ethtool/bitset.c index f0883357d12e..4691d6d0f2b7 100644 --- a/net/ethtool/bitset.c +++ b/net/ethtool/bitset.c @@ -91,7 +91,7 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, u32 mask; if (end <= start) - return true; + return false; if (start % 32) { mask = ethnl_upper_bits(start); @@ -104,11 +104,11 @@ static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start, start_word++; } - if (!memchr_inv(map + start_word, '\0', - (end_word - start_word) * sizeof(u32))) + if (memchr_inv(map + start_word, '\0', + (end_word - start_word) * sizeof(u32))) return true; if (end % 32 == 0) - return true; + return false; return map[end_word] & ethnl_lower_bits(end); } From a184aec790135938b0fadb415e55accd1f8685a0 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 28 Apr 2026 20:21:38 +0300 Subject: [PATCH 271/697] drm/msm/dsi: don't dump registers past the mapped region [ Upstream commit 5b49a46baa853b26dbefa65c6c75dd9ff69f63d4 ] On DSI 6G platforms the IO address space is internally adjusted by io_offset. Later this adjusted address might be used for memory dumping. However the size that is used for memory dumping isn't adjusted to account for the io_offset, leading to the potential access to the unmapped region. Lower ctrl_size by the io_offset value to prevent access past the mapped area. msm_disp_snapshot_add_block+0x1d4/0x3c8 [msm] (P) msm_dsi_host_snapshot+0x4c/0x78 [msm] msm_dsi_snapshot+0x28/0x50 [msm] msm_disp_snapshot_capture_state+0x74/0x140 [msm] msm_disp_snapshot_state_sync+0x60/0x90 [msm] _msm_disp_snapshot_work+0x30/0x90 [msm] kthread_worker_fn+0xdc/0x460 kthread+0x120/0x140 Fixes: bac2c6a62ed9 ("drm/msm: get rid of msm_iomap_size") Signed-off-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Patchwork: https://patchwork.freedesktop.org/patch/721747/ Link: https://lore.kernel.org/r/20260428-msm-fix-dsi-dump-v1-1-5d4cb5ccfac7@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/dsi/dsi_host.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 1c0841a1c101..50474c994d47 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -2003,6 +2003,7 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) /* fixup base address by io offset */ msm_host->ctrl_base += cfg->io_offset; + msm_host->ctrl_size -= cfg->io_offset; ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, cfg->regulator_data, From ff58e5ef1b46ce614af048d2d04986df05ffab90 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 5 May 2026 03:24:58 +0300 Subject: [PATCH 272/697] drm/msm/dpu: don't mix devm and drmm functions [ Upstream commit c0c70a11365cba7fba25a77463582bcec0f7846e ] Mixing devm and drmm functions will result in a use-after-free on msm driver teardown if userspace keeps a reference on the drm device: The WB connector data will be destroyed because of the use of devm_kzalloc()), while the usersoace still can try interacting with the WB connector (which uses drmm_ functions). Change dpu_writeback_init() to use drmm_. Fixes: 0b37ac63fc9d ("drm/msm/dpu: use drmm_writeback_connector_init()") Reported-by: Christophe JAILLET Closes: https://lore.kernel.org/r/78c764b8-44cf-4db5-88e7-807a85954518@wanadoo.fr Signed-off-by: Dmitry Baryshkov Reviewed-by: John.Harrison@Igalia.com Patchwork: https://patchwork.freedesktop.org/patch/722656/ Link: https://lore.kernel.org/r/20260505-wb-drop-encoder-v5-1-42567b7c7af2@oss.qualcomm.com Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c index 7545c0293efb..6f2370c9dd98 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c @@ -5,6 +5,7 @@ #include #include +#include #include "dpu_writeback.h" @@ -125,7 +126,7 @@ int dpu_writeback_init(struct drm_device *dev, struct drm_encoder *enc, struct dpu_wb_connector *dpu_wb_conn; int rc = 0; - dpu_wb_conn = devm_kzalloc(dev->dev, sizeof(*dpu_wb_conn), GFP_KERNEL); + dpu_wb_conn = drmm_kzalloc(dev, sizeof(*dpu_wb_conn), GFP_KERNEL); if (!dpu_wb_conn) return -ENOMEM; From 15dba511d5694dea40dce263be3fa655a1a65f3f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 13 May 2026 18:19:40 +0800 Subject: [PATCH 273/697] selftests: ublk: cap nthreads to kernel's actual nr_hw_queues [ Upstream commit 87d0740b7c4cc847be1b6f307ab6d8547cb1a726 ] dev->nthreads is derived from the user-requested queue count before the ADD command, but the kernel may reduce nr_hw_queues (capped to nr_cpu_ids). When the VM has fewer CPUs than requested queues, the daemon creates more handler threads than there are kernel queues. In non-batch mode, the extra threads access uninitialized queues (q_depth=0), submit zero io_uring SQEs, and block forever in io_cqring_wait. In batch mode, the extra threads cause similar hangs during device removal. In both cases, the stuck threads prevent the daemon from closing the char device, holding the last ublk_device reference and causing ublk_ctrl_del_dev() to hang in wait_event_interruptible(). Fix by capping dev->nthreads to the kernel-returned nr_hw_queues after the ADD command completes. per_io_tasks mode is excluded because threads interleave across all queues, so nthreads > nr_hw_queues is valid. Fixes: abe54c160346 ("selftests: ublk: kublk: decouple ublk_queues from ublk server threads") Signed-off-by: Ming Lei Link: https://patch.msgid.link/20260513101941.1373998-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- tools/testing/selftests/ublk/kublk.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c index cbd23444c8a9..ac47979349a4 100644 --- a/tools/testing/selftests/ublk/kublk.c +++ b/tools/testing/selftests/ublk/kublk.c @@ -1220,6 +1220,17 @@ static int __cmd_dev_add(const struct dev_ctx *ctx) goto fail; } + /* + * The kernel may reduce nr_hw_queues (e.g. capped to nr_cpu_ids). + * Cap nthreads to the actual queue count to avoid creating extra + * handler threads that will hang during device removal. + * + * per_io_tasks mode is excluded: threads interleave across all + * queues so nthreads > nr_hw_queues is valid and intentional. + */ + if (!ctx->per_io_tasks && dev->nthreads > info->nr_hw_queues) + dev->nthreads = info->nr_hw_queues; + ret = ublk_start_daemon(ctx, dev); ublk_dbg(UBLK_DBG_DEV, "%s: daemon exit %d\n", __func__, ret); if (ret < 0) From 3ad2d8be6e4ddf7d26d27f56e2f58b9b81537f2c Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Mon, 16 Mar 2026 16:12:00 +0100 Subject: [PATCH 274/697] x86/mce: Restore MCA polling interval halving [ Upstream commit ea324444ece9f301b5c4ff71b258cc68990c4d61 ] RongQing reported that the MCA polling interval doesn't halve when an error gets logged. It was traced down to the commit in Fixes:, because: mce_timer_fn() |-> mce_poll_banks() |-> machine_check_poll() |-> mce_log() which will queue the work and return. Now, back in mce_timer_fn(): /* * Alert userspace if needed. If we logged an MCE, reduce the polling * interval, otherwise increase the polling interval. */ if (mce_notify_irq()) <--- here we haven't ran the notifier chain yet so mce_need_notify is not set yet so this won't hit and we won't halve the interval iv. Now the notifier chain runs. mce_early_notifier() sets the bit, does mce_notify_irq(), that clears the bit and then the notifier chain a little later logs the error. So this is a silly timing issue. But, that's all unnecessary. All it needs to happen here is, the "should we notify of a logged MCE" mce_notify_irq() asks, should be simply a question to the mce gen pool: "Are you empty?" And that then turns into a simple yes or no answer and it all JustWorks(tm). So do that and also distribute the functionality where it belongs: - Print that MCE events have been logged in mce_log() - Trigger the mcelog tool specific work in the first notifier As a result, mce_notify_irq() can go now. Fixes: 011d82611172 ("RAS: Add a Corrected Errors Collector") Reported-by: Li RongQing Signed-off-by: Borislav Petkov (AMD) Reviewed-by: Qiuxu Zhuo Tested-by: Qiuxu Zhuo Link: https://lore.kernel.org/r/20260112082747.2842-1-lirongqing@baidu.com Signed-off-by: Sasha Levin --- arch/x86/kernel/cpu/mce/core.c | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c index 460e90a1a0b1..c8b112c6c549 100644 --- a/arch/x86/kernel/cpu/mce/core.c +++ b/arch/x86/kernel/cpu/mce/core.c @@ -89,7 +89,6 @@ struct mca_config mca_cfg __read_mostly = { }; static DEFINE_PER_CPU(struct mce_hw_err, hw_errs_seen); -static unsigned long mce_need_notify; /* * MCA banks polled by the period polling timer for corrected events. @@ -151,8 +150,10 @@ EXPORT_PER_CPU_SYMBOL_GPL(injectm); void mce_log(struct mce_hw_err *err) { - if (mce_gen_pool_add(err)) + if (mce_gen_pool_add(err)) { + pr_info(HW_ERR "Machine check events logged\n"); irq_work_queue(&mce_irq_work); + } } EXPORT_SYMBOL_GPL(mce_log); @@ -584,28 +585,6 @@ bool mce_is_correctable(struct mce *m) } EXPORT_SYMBOL_GPL(mce_is_correctable); -/* - * Notify the user(s) about new machine check events. - * Can be called from interrupt context, but not from machine check/NMI - * context. - */ -static bool mce_notify_irq(void) -{ - /* Not more than two messages every minute */ - static DEFINE_RATELIMIT_STATE(ratelimit, 60*HZ, 2); - - if (test_and_clear_bit(0, &mce_need_notify)) { - mce_work_trigger(); - - if (__ratelimit(&ratelimit)) - pr_info(HW_ERR "Machine check events logged\n"); - - return true; - } - - return false; -} - static int mce_early_notifier(struct notifier_block *nb, unsigned long val, void *data) { @@ -617,9 +596,7 @@ static int mce_early_notifier(struct notifier_block *nb, unsigned long val, /* Emit the trace record: */ trace_mce_record(err); - set_bit(0, &mce_need_notify); - - mce_notify_irq(); + mce_work_trigger(); return NOTIFY_DONE; } @@ -1771,7 +1748,7 @@ static void mce_timer_fn(struct timer_list *t) * Alert userspace if needed. If we logged an MCE, reduce the polling * interval, otherwise increase the polling interval. */ - if (mce_notify_irq()) + if (!mce_gen_pool_empty()) iv = max(iv / 2, (unsigned long) HZ/100); else iv = min(iv * 2, round_jiffies_relative(check_interval * HZ)); From 3a7b59d2385d477526cb5b5822754def29953ac0 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Fri, 24 Apr 2026 14:41:13 -0700 Subject: [PATCH 275/697] Documentation: intel_pstate: Fix description of asymmetric packing with SMT [ Upstream commit ee047fc7a2da90554410128195058c409a391d43 ] Patchset [1], including commits 046a5a95c3b0 ("x86/sched/itmt: Give all SMT siblings of a core the same priority") 995998ebdebd ("x86/sched: Remove SD_ASYM_PACKING from the SMT domain flags") overhauled asym_packing handling in the scheduler on x86 hybrid processors with SMT. It removed SD_ASYM_PACKING from the x86 SMT scheduling domain and made all SMT siblings of a core share the same priority. As a result, asym_packing operates only across physical cores, spreading tasks among them and only using idle SMT siblings once all physical cores are busy. Fix the documentation to reflect this behavior. Fixes: f20af84c29b2 ("cpufreq: intel_pstate: Document hybrid processor support") Link: https://lore.kernel.org/r/20230406203148.19182-1-ricardo.neri-calderon@linux.intel.com [1] Signed-off-by: Ricardo Neri [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260424-rneri-fix-intel-pstate-doc-smt-asym-packing-v1-1-317bf7d5c362@linux.intel.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin --- Documentation/admin-guide/pm/intel_pstate.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst index 26e702c7016e..66287f8d645f 100644 --- a/Documentation/admin-guide/pm/intel_pstate.rst +++ b/Documentation/admin-guide/pm/intel_pstate.rst @@ -348,11 +348,12 @@ HyperThreading (HT) in the context of Intel processors, is enabled on at least one core, ``intel_pstate`` assigns performance-based priorities to CPUs. Namely, the priority of a given CPU reflects its highest HWP performance level which causes the CPU scheduler to generally prefer more performant CPUs, so the less -performant CPUs are used when the other ones are fully loaded. However, SMT -siblings (that is, logical CPUs sharing one physical core) are treated in a -special way such that if one of them is in use, the effective priority of the -other ones is lowered below the priorities of the CPUs located in the other -physical cores. +performant CPUs are used when the other ones are fully loaded. SMT siblings +(that is, logical CPUs sharing one physical core) are given the same priority. +The scheduler can pull tasks from lower-priority cores and place them on any +sibling. Since the scheduler spreads tasks among physical cores, tasks will be +placed on the SMT siblings of physical cores only after all physical cores are +busy. This approach maximizes performance in the majority of cases, but unfortunately it also leads to excessive energy usage in some important scenarios, like video From eea43d5ed45089705bc5d70971c39076962d5951 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 11 Apr 2026 17:59:15 +0300 Subject: [PATCH 276/697] drm/msm/adreno: fix userspace-triggered crash on a2xx-a4xx [ Upstream commit 2b4abf879360ea00a9e2b46d2d15dcdbc0687eed ] Before a5xx Adreno driver will not try fetching UBWC params (because those generations didn't support UBWC anyway), however it's still possible to query UBWC-related params from the userspace, triggering possible NULL pointer dereference. Check for UBWC config in adreno_get_param() and return sane defaults if there is none. Fixes: a452510aad53 ("drm/msm/adreno: Switch to the common UBWC config struct") Signed-off-by: Dmitry Baryshkov Reviewed-by: Rob Clark Patchwork: https://patchwork.freedesktop.org/patch/717778/ Message-ID: <20260411-adreno-fix-ubwc-v3-1-4983156f3f80@oss.qualcomm.com> Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 4b5a4edd0702..056a9e18cd4a 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -419,15 +419,21 @@ int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx, *value = vm->mm_range; return 0; case MSM_PARAM_HIGHEST_BANK_BIT: + if (!adreno_gpu->ubwc_config) + return UERR(ENOENT, drm, "no UBWC on this platform"); *value = adreno_gpu->ubwc_config->highest_bank_bit; return 0; case MSM_PARAM_RAYTRACING: *value = adreno_gpu->has_ray_tracing; return 0; case MSM_PARAM_UBWC_SWIZZLE: + if (!adreno_gpu->ubwc_config) + return UERR(ENOENT, drm, "no UBWC on this platform"); *value = adreno_gpu->ubwc_config->ubwc_swizzle; return 0; case MSM_PARAM_MACROTILE_MODE: + if (!adreno_gpu->ubwc_config) + return UERR(ENOENT, drm, "no UBWC on this platform"); *value = adreno_gpu->ubwc_config->macrotile_mode; return 0; case MSM_PARAM_UCHE_TRAP_BASE: From f4e37f3df436c2bdd2621c21f9c72c8f149a221d Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Tue, 21 Apr 2026 13:02:38 +0900 Subject: [PATCH 277/697] drm/msm: Fix iommu_map_sgtable() return value check and avoid WARN [ Upstream commit 55e0f0d1c1a4ee1e46da7da4d443eb3044fb3851 ] Commit "iommu: return full error code from iommu_map_sg[_atomic]()" changed iommu_map_sgtable() to return an ssize_t and negative values in error cases, rather than a size_t and a zero. Store the return value in the appropriate type and in case of error, return it rather than WARNing. Fixes: ad8f36e4b6b1 ("iommu: return full error code from iommu_map_sg[_atomic]()") Signed-off-by: Mikko Perttunen Patchwork: https://patchwork.freedesktop.org/patch/719685/ Message-ID: <20260421-iommu_map_sgtable-return-v1-3-fb484c07d2a1@nvidia.com> Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/msm_iommu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index a188617653e8..82a172f21a3e 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -677,7 +677,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, int prot) { struct msm_iommu *iommu = to_msm_iommu(mmu); - size_t ret; + ssize_t ret; WARN_ON(off != 0); @@ -686,7 +686,8 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, iova |= GENMASK_ULL(63, 49); ret = iommu_map_sgtable(iommu->domain, iova, sgt, prot); - WARN_ON(!ret); + if (ret < 0) + return ret; return (ret == len) ? 0 : -EINVAL; } From 6dcd072a5ae3aed336e4a67a7d4cc5205b240065 Mon Sep 17 00:00:00 2001 From: Sayali Patil Date: Wed, 13 May 2026 13:44:13 +0530 Subject: [PATCH 278/697] powerpc/time: Remove redundant preempt_disable|enable() calls from arch_irq_work_raise() [ Upstream commit 31467b23823ffec1f6fff407f8e3ca9af8b7491a ] A kernel panic is observed when handling machine check exceptions from real mode. BUG: Unable to handle kernel data access on read at 0xc00000006be21300 Oops: Kernel access of bad area, sig: 11 [#1] MSR: 8000000000001003 CR: 88222248 XER: 00000005 CFAR: c00000000003ffc4 DAR: c00000006be21300 DSISR: 40000000 IRQMASK: 0 NIP [c000000000029e40] arch_irq_work_raise+0x10/0x70 LR [c00000000003ffc8] machine_check_queue_event+0xa8/0x150 Call Trace: [c0000000179d3c70] [c00000000003ff64] machine_check_queue_event+0x44/0x150 [c0000000179d3d30] [c0000000000084e0] machine_check_early_common+0x1f0/0x2c0 The crash occurs because arch_irq_work_raise() calls preempt_disable() from machine check exception (MCE) handlers running in real mode. In this context, accessing the preempt_count can fault, leading to the panic. The preempt_disable()/preempt_enable() pair in arch_irq_work_raise() was originally added by commit 0fe1ac48bef0 ("powerpc/perf_event: Fix oops due to perf_event_do_pending call") to avoid races while raising irq work from exception context. Later, commit 471ba0e686cb ("irq_work: Do not raise an IPI when queueing work on the local CPU") added preemption protection in irq_work_queue() path, while commit 20b876918c06 ("irq_work: Use per cpu atomics instead of regular atomics") added equivalent protection in irq_work_queue_on() before reaching arch_irq_work_raise(): irq_work_queue() / irq_work_queue_on() -> preempt_disable() -> __irq_work_queue_local() -> irq_work_raise() -> arch_irq_work_raise() As a result, callers other than mce_irq_work_raise() already execute with preemption disabled, making the additional preempt_disable()/preempt_enable() pair in arch_irq_work_raise() redundant. The arch_irq_work_raise() function executes in NMI context when called from MCE handler. Hence we will not be preempted or scheduled out since we are in NMI context with MSR[EE]=0. Therefore, it is safe to remove the preempt_disable()/preempt_enable() calls from here. Remove it to avoid accessing preempt_count from real mode context. Fixes: cc15ff327569 ("powerpc/mce: Avoid using irq_work_queue() in realmode") Suggested-by: Mahesh Salgaonkar Acked-by: Shrikanth Hegde Reviewed-by: Ritesh Harjani (IBM) Signed-off-by: Sayali Patil [Maddy: Fixed the commit title] Signed-off-by: Madhavan Srinivasan Link: https://patch.msgid.link/20260513081413.222490-1-sayalip@linux.ibm.com Signed-off-by: Sasha Levin --- arch/powerpc/kernel/time.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 4bbeb8644d3d..b4472288e0d4 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -458,6 +458,10 @@ DEFINE_PER_CPU(u8, irq_work_pending); #endif /* 32 vs 64 bit */ +/* + * Must be called with preemption disabled since it updates + * per-CPU irq_work state and programs the local CPU decrementer. + */ void arch_irq_work_raise(void) { /* @@ -471,10 +475,8 @@ void arch_irq_work_raise(void) * which could get tangled up if we're messing with the same state * here. */ - preempt_disable(); set_irq_work_pending_flag(); set_dec(1); - preempt_enable(); } static void set_dec_or_work(u64 val) From afa9036b8c9963947b487c36e332df6a42c96fcb Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Sun, 10 May 2026 23:21:38 -0700 Subject: [PATCH 279/697] net/smc: reject CHID-0 ACCEPT that matches an empty ism_dev slot [ Upstream commit 277740023def559a4a2ddc3e8e784ee37a0f16a9 ] On the SMC-D client, slot 0 of ini->ism_dev[]/ini->ism_chid[] is reserved for an SMC-Dv1 device. smc_find_ism_v2_device_clnt() populates V2 entries starting at index 1, so when no V1 device is selected slot 0 is left in its kzalloc()'ed state with ism_dev[0] == NULL and ism_chid[0] == 0. smc_v2_determine_accepted_chid() then matches the peer's CHID against the array starting from index 0 using the CHID alone. A malicious peer replying to a SMC-Dv2-only proposal with d1.chid == 0 matches the empty slot, ini->ism_selected becomes 0, and the subsequent ism_dev[0]->lgr_lock dereference in smc_conn_create() faults at offsetof(struct smcd_dev, lgr_lock) == 0x68: BUG: KASAN: null-ptr-deref in _raw_spin_lock_bh+0x79/0xe0 Write of size 4 at addr 0000000000000068 by task exploit/144 Call Trace: _raw_spin_lock_bh smc_conn_create (net/smc/smc_core.c:1997) __smc_connect (net/smc/af_smc.c:1447) smc_connect (net/smc/af_smc.c:1720) __sys_connect __x64_sys_connect do_syscall_64 Require ism_dev[i] to be non-NULL before accepting a CHID match. Fixes: a7c9c5f4af7f ("net/smc: CLC accept / confirm V2") Reported-by: Weiming Shi Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Xiang Mei Link: https://patch.msgid.link/20260511062138.2839584-1-xmei5@asu.edu Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/smc/af_smc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 6421c2e1c84d..5915fcdef743 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1400,7 +1400,8 @@ smc_v2_determine_accepted_chid(struct smc_clc_msg_accept_confirm *aclc, int i; for (i = 0; i < ini->ism_offered_cnt + 1; i++) { - if (ini->ism_chid[i] == ntohs(aclc->d1.chid)) { + if (ini->ism_dev[i] && + ini->ism_chid[i] == ntohs(aclc->d1.chid)) { ini->ism_selected = i; return 0; } From eca989eab4b2599dcb02f72140a7c08f08838520 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 11 May 2026 10:49:17 -0700 Subject: [PATCH 280/697] net: tls: fix off-by-one in sg_chain entry count for wrapped sk_msg ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 285943c6e7ca309bbea84b253745154241d9788a ] When an sk_msg scatterlist ring wraps (sg.end < sg.start), tls_push_record() chains the tail portion of the ring to the head using sg_chain(). An extra entry in the sg array is reserved for this: struct sk_msg_sg { [...] /* The extra two elements: * 1) used for chaining the front and sections when the list becomes * partitioned (e.g. end < start). The crypto APIs require the * chaining; * 2) to chain tailer SG entries after the message. */ struct scatterlist data[MAX_MSG_FRAGS + 2]; The current code uses MAX_SKB_FRAGS + 1 as the ring size: sg_chain(&msg_pl->sg.data[msg_pl->sg.start], MAX_SKB_FRAGS - msg_pl->sg.start + 1, msg_pl->sg.data); This places the chain pointer at sg_chain(data[start], (MAX_SKB_FRAGS - msg_start + 1) .. = &data[start] + (MAX_SKB_FRAGS - msg_start + 1) - 1 = data[start + (MAX_SKB_FRAGS - start + 1) - 1] = data[MAX_SKB_FRAGS] instead of the true last entry. This is likely due to a "race" of the commit under Fixes landing close to commit 031097d9e079 ("bpf: sk_msg, zap ingress queue on psock down") Convert to ARRAY_SIZE and drop the data[start] / - start (as suggested by Sabrina). Reported-by: 钱一铭 Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") Signed-off-by: Jakub Kicinski Reviewed-by: Sabrina Dubroca Link: https://patch.msgid.link/20260511174920.433155-2-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index f2ea190777f0..1ad5d0e7ae27 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -800,11 +800,9 @@ static int tls_push_record(struct sock *sk, int flags, sg_mark_end(sk_msg_elem(msg_pl, i)); } - if (msg_pl->sg.end < msg_pl->sg.start) { - sg_chain(&msg_pl->sg.data[msg_pl->sg.start], - MAX_SKB_FRAGS - msg_pl->sg.start + 1, + if (msg_pl->sg.end < msg_pl->sg.start) + sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), msg_pl->sg.data); - } i = msg_pl->sg.start; sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); From af855f4c966afafef74faf8390c7b86568c0d46d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 11 May 2026 10:49:18 -0700 Subject: [PATCH 281/697] net: tls: prevent chain-after-chain in plain text SG [ Upstream commit ff26a0e8377dec07e4a7230db7675bed1b9a6d03 ] Sashiko points out that if end = 0 (start != 0) the current code will create a chain link to content type right after the wrap link: This would create a chain where the wrap link points directly to another chain link. The scatterlist API sg_next iterator does not recursively resolve consecutive chain links. meaning this is illegal input to crypto. The wrapping link is unnecessary if end = 0. end is the entry after the last one used so end = 0 means there's nothing pushed after the wrap: end start i v v v [ ]...[ ][ d ][ d ][ d ][ d ][rsv for wrap] Skip the wrapping in this case. TLS 1.3 can use the "wrapping slot" for it's chaining if end = 0. This avoids the chain-after-chain. Move the wrap chaining before marking END and chaining off content type, that feels like more logical ordering to me, but should not matter from functional perspective. Reported-by: Sashiko Fixes: 9aaaa56845a0 ("bpf: Sockmap/tls, skmsg can have wrapped skmsg that needs extra chaining") Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260511174920.433155-3-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 1ad5d0e7ae27..b28eb04075d1 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -789,21 +789,33 @@ static int tls_push_record(struct sock *sk, int flags, i = msg_pl->sg.end; sk_msg_iter_var_prev(i); + /* msg_pl->sg.data is a ring; data[MAX+1] is reserved for the wrap + * link (frags won't use it). 'i' is now the last filled entry: + * + * i end start + * v v v [ rsv ] + * [ d ][ d ][ ][ ]...[ ][ d ][ d ][ d ][chain] + * ^ END v + * `-----------------------------------------' + * + * Note that SGL does not allow chain-after-chain, so for TLS 1.3, + * we must make sure we don't create the wrap entry and then chain + * link to content_type immediately at index 0. + */ + if (i < msg_pl->sg.start) + sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), + msg_pl->sg.data); + rec->content_type = record_type; if (prot->version == TLS_1_3_VERSION) { /* Add content type to end of message. No padding added */ sg_set_buf(&rec->sg_content_type, &rec->content_type, 1); sg_mark_end(&rec->sg_content_type); - sg_chain(msg_pl->sg.data, msg_pl->sg.end + 1, - &rec->sg_content_type); + sg_chain(msg_pl->sg.data, i + 2, &rec->sg_content_type); } else { sg_mark_end(sk_msg_elem(msg_pl, i)); } - if (msg_pl->sg.end < msg_pl->sg.start) - sg_chain(msg_pl->sg.data, ARRAY_SIZE(msg_pl->sg.data), - msg_pl->sg.data); - i = msg_pl->sg.start; sg_chain(rec->sg_aead_in, 2, &msg_pl->sg.data[i]); From 2bc34520ce5cf0c49151fe5f1fa403f5f097751c Mon Sep 17 00:00:00 2001 From: Sven Schuchmann Date: Tue, 12 May 2026 09:19:47 +0200 Subject: [PATCH 282/697] net: phy: DP83TC811: add reading of abilities [ Upstream commit c78bdba7b9666020c0832150a4fc4c0aebc7c6ac ] At this time the driver is not listing any speeds it supports. This should be ETHTOOL_LINK_MODE_100baseT1_Full_BIT for DP83TC811. Add the missing call for phylib to read the abilities. Fixes: b753a9faaf9a ("net: phy: DP83TC811: Introduce support for the DP83TC811 phy") Suggested-by: Andrew Lunn Signed-off-by: Sven Schuchmann Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260512071949.6218-1-schuchmann@schleissheimer.de [pabeni@redhat.com: dropped revision history] Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/phy/dp83tc811.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/phy/dp83tc811.c b/drivers/net/phy/dp83tc811.c index e480c2a07450..252fb12b3e68 100644 --- a/drivers/net/phy/dp83tc811.c +++ b/drivers/net/phy/dp83tc811.c @@ -393,6 +393,7 @@ static struct phy_driver dp83811_driver[] = { .config_init = dp83811_config_init, .config_aneg = dp83811_config_aneg, .soft_reset = dp83811_phy_reset, + .get_features = genphy_c45_pma_read_ext_abilities, .get_wol = dp83811_get_wol, .set_wol = dp83811_set_wol, .config_intr = dp83811_config_intr, From e5460eb7238c19d651a9b22b2378b587033a4095 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 13 May 2026 11:55:20 +0100 Subject: [PATCH 283/697] ovpn: tcp - use cached peer pointer in ovpn_tcp_close() [ Upstream commit 775d8d7ad02aa345e1588424a6a8b9ae49fb9012 ] ovpn_tcp_close() loads the ovpn_socket via rcu_dereference_sk_user_data() under rcu_read_lock(), takes a reference on sock->peer, caches the peer pointer in a local, and drops the read lock. It then passes sock->peer (rather than the cached local) to ovpn_peer_del(), re-dereferencing the ovpn_socket after the RCU read section has ended. Unlike ovpn_tcp_sendmsg(), which uses the same "load under RCU, use after unlock" pattern but is protected by lock_sock() held across the function, ovpn_tcp_close() runs without the socket lock: inet_release() invokes sk_prot->close() without taking lock_sock first. ovpn_socket_release() can therefore complete its kref_put -> detach -> synchronize_rcu -> kfree(sock) sequence concurrently, in the window after ovpn_tcp_close() drops rcu_read_lock() but before it dereferences sock->peer. The synchronize_rcu() in ovpn_socket_release() protects readers that use the dereferenced pointer inside the RCU read section, not those that escape the pointer to a local and use it afterwards. A reproducer follows the pattern of commit 94560267d6c4 ("ovpn: tcp - don't deref NULL sk_socket member after tcp_close()"): trigger a peer removal (keepalive expiration or netlink OVPN_CMD_DEL_PEER) at the same moment userspace closes the TCP fd. That commit fixed the detach-side of the same race window; this one fixes the close-side at a different victim. Tighten the entry block to read sock->peer exactly once into the cached peer local, and route all subsequent uses (the hold check, the ovpn_peer_del() call, and the prot->close() invocation) through that local. sock->peer is only ever written once in ovpn_socket_new() under lock_sock(), before rcu_assign_sk_user_data() publishes the ovpn_socket, and is never reassigned afterwards - but the previous multi-read pattern made that invariant implicit rather than explicit. The same multi-read shape exists in ovpn_tcp_recvmsg(), ovpn_tcp_sendmsg(), ovpn_tcp_data_ready() and ovpn_tcp_write_space(); those will be cleaned up via a dedicated helper in a follow-up net-next series. Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Reviewed-by: Sabrina Dubroca Assisted-by: Claude:claude-opus-4-7 Signed-off-by: David Carlier Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/tcp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 5499c1572f3e..5f345ae7d59d 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -581,14 +581,19 @@ static void ovpn_tcp_close(struct sock *sk, long timeout) rcu_read_lock(); sock = rcu_dereference_sk_user_data(sk); - if (!sock || !sock->peer || !ovpn_peer_hold(sock->peer)) { + if (!sock) { rcu_read_unlock(); return; } + peer = sock->peer; + if (!peer || !ovpn_peer_hold(peer)) { + rcu_read_unlock(); + return; + } rcu_read_unlock(); - ovpn_peer_del(sock->peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); + ovpn_peer_del(peer, OVPN_DEL_PEER_REASON_TRANSPORT_DISCONNECT); peer->tcp.sk_cb.prot->close(sk, timeout); ovpn_peer_put(peer); } From 8298834912d76dbc82c12b6b4ab7590ed2bb8ae5 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 13 May 2026 11:55:21 +0100 Subject: [PATCH 284/697] ovpn: respect peer refcount in CMD_NEW_PEER error path [ Upstream commit 1fef6614673ff0846d30acdeeaf3cf98bb5f6116 ] ovpn_nl_peer_new_doit()'s error path calls ovpn_peer_release() directly rather than ovpn_peer_put(), bypassing the kref. The accompanying comment ("peer was not yet hashed, thus it is not used in any context") holds for UDP but not for TCP. For UDP, the ovpn_socket union uses the .ovpn arm and never points back at a peer; UDP encap_recv looks up peers via the not-yet-populated hashtables, so the new peer is unreachable until ovpn_peer_add() publishes it. For TCP, ovpn_socket_new() sets ovpn_sock->peer and ovpn_tcp_socket_attach() publishes ovpn_sock via rcu_assign_sk_user_data(). From that moment until ovpn_socket_release() detaches in the error path, the TCP fd is fully wired: userspace recvmsg / sendmsg / close / poll on the fd, as well as the strparser-driven ovpn_tcp_rcv() path, can reach the peer through sk_user_data -> ovpn_sock->peer and bump its refcount via ovpn_peer_hold(). ovpn_tcp_socket_wait_finish() (called inside ovpn_socket_release()) drains strparser and the tx work, but does not synchronize with userspace syscall callers that already hold a peer reference. If ovpn_nl_peer_modify() or ovpn_peer_add() returns an error while such a caller is in flight - notably an ovpn_tcp_recvmsg() blocked in __skb_recv_datagram() on peer->tcp.user_queue - the direct ovpn_peer_release() destroys the peer while the caller still holds the reference, and the eventual ovpn_peer_put() from that caller operates on freed memory. Replace the direct destructor call with ovpn_peer_put() so the kref correctly defers destruction until the last reference is dropped. In the common case where no concurrent user is present, behaviour is unchanged: the kref hits zero immediately and ovpn_peer_release_kref() runs the same destructor. With this conversion ovpn_peer_release() has no callers outside peer.c - ovpn_peer_release_kref() in the same translation unit is the only remaining user - so make it static and drop its declaration from peer.h. Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Reviewed-by: Sabrina Dubroca Assisted-by: Claude:claude-opus-4-7 Signed-off-by: David Carlier Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/netlink.c | 8 +++++--- drivers/net/ovpn/peer.c | 2 +- drivers/net/ovpn/peer.h | 1 - 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index c7f382437630..bdb56ef0c904 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -455,10 +455,12 @@ int ovpn_nl_peer_new_doit(struct sk_buff *skb, struct genl_info *info) sock_release: ovpn_socket_release(peer); peer_release: - /* release right away because peer was not yet hashed, thus it is not - * used in any context + /* For UDP, the peer is unreachable until added to the hashtables, so + * dropping the initial reference is enough. For TCP, the peer may be + * concurrently reachable via sk_user_data->peer until + * ovpn_socket_release() detaches; rely on the refcount. */ - ovpn_peer_release(peer); + ovpn_peer_put(peer); return ret; } diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 4bfcab0c8652..a3d856724b84 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -348,7 +348,7 @@ static void ovpn_peer_release_rcu(struct rcu_head *head) * ovpn_peer_release - release peer private members * @peer: the peer to release */ -void ovpn_peer_release(struct ovpn_peer *peer) +static void ovpn_peer_release(struct ovpn_peer *peer) { ovpn_crypto_state_release(&peer->crypto); spin_lock_bh(&peer->lock); diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index a1423f2b09e0..4de5aeae33f7 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -125,7 +125,6 @@ static inline bool ovpn_peer_hold(struct ovpn_peer *peer) return kref_get_unless_zero(&peer->refcount); } -void ovpn_peer_release(struct ovpn_peer *peer); void ovpn_peer_release_kref(struct kref *kref); /** From 097d62df38314c14b88fab9096f3461baf158e2b Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 17 Mar 2026 14:47:56 +0100 Subject: [PATCH 285/697] ovpn: fix race between deleting interface and adding new peer [ Upstream commit 982422b11e6f95f766a8cd2c2b1cbdb77e234a61 ] While deleting an existing ovpn interface, there is a very narrow window where adding a new peer via netlink may cause the netdevice to hang and prevent its unregistration. It may happen during ovpn_dellink(), when all existing peers are freed and the device is queued for deregistration, but a CMD_PEER_NEW message comes in adding a new peer that takes again a reference to the netdev. At this point there is no way to release the device because we are under the assumption that all peers were already released. Fix the race condition by releasing all peers in ndo_uninit(), when the netdevice has already been removed from the netdev list. Also ovpn_peer_add() has now an extra check that forces the function to bail out if the device reg_state is not REGISTERED. This way any incoming CMD_PEER_NEW racing with the interface deletion routine will simply stop before adding the peer. Note that the above check happens while holding the netdev_lock to prevent racing netdev state changes. ovpn_dellink() is now empty and can be removed. Reported-by: Hyunwoo Kim Closes: https://lore.kernel.org/netdev/aaVgJ16edTfQkYbx@v4bel/ Suggested-by: Sabrina Dubroca Fixes: 80747caef33d ("ovpn: introduce the ovpn_peer object") Reviewed-by: Sabrina Dubroca Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/main.c | 12 ++---------- drivers/net/ovpn/peer.c | 21 ++++++++++++++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 1bb1afe766a4..3f76b1b0e5f6 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -92,6 +92,8 @@ static void ovpn_net_uninit(struct net_device *dev) { struct ovpn_priv *ovpn = netdev_priv(dev); + disable_delayed_work_sync(&ovpn->keepalive_work); + ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); gro_cells_destroy(&ovpn->gro_cells); } @@ -208,15 +210,6 @@ static int ovpn_newlink(struct net_device *dev, return register_netdevice(dev); } -static void ovpn_dellink(struct net_device *dev, struct list_head *head) -{ - struct ovpn_priv *ovpn = netdev_priv(dev); - - cancel_delayed_work_sync(&ovpn->keepalive_work); - ovpn_peers_free(ovpn, NULL, OVPN_DEL_PEER_REASON_TEARDOWN); - unregister_netdevice_queue(dev, head); -} - static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct ovpn_priv *ovpn = netdev_priv(dev); @@ -235,7 +228,6 @@ static struct rtnl_link_ops ovpn_link_ops = { .policy = ovpn_policy, .maxtype = IFLA_OVPN_MAX, .newlink = ovpn_newlink, - .dellink = ovpn_dellink, .fill_info = ovpn_fill_info, }; diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a3d856724b84..87a83321f1dd 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1029,14 +1029,29 @@ static int ovpn_peer_add_p2p(struct ovpn_priv *ovpn, struct ovpn_peer *peer) */ int ovpn_peer_add(struct ovpn_priv *ovpn, struct ovpn_peer *peer) { + int ret = -ENODEV; + + /* Prevent adding new peers while destroying the ovpn interface. + * Failing to do so would end up holding the device reference + * endlessly hostage of the new peer object with no chance of + * release.. + */ + netdev_lock(ovpn->dev); + if (ovpn->dev->reg_state != NETREG_REGISTERED) + goto out; + switch (ovpn->mode) { case OVPN_MODE_MP: - return ovpn_peer_add_mp(ovpn, peer); + ret = ovpn_peer_add_mp(ovpn, peer); + break; case OVPN_MODE_P2P: - return ovpn_peer_add_p2p(ovpn, peer); + ret = ovpn_peer_add_p2p(ovpn, peer); + break; } +out: + netdev_unlock(ovpn->dev); - return -EOPNOTSUPP; + return ret; } /** From 2378d25675dacf29b2074dd5a010d7883f6a621a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 14 Mar 2026 14:24:56 +0100 Subject: [PATCH 286/697] gcc-plugins: Always define CONST_CAST_GIMPLE and CONST_CAST_TREE [ Upstream commit 905c559e51497b8bfdbb68df8be56d2f70f0de8e ] For gcc-16, the CONST_CAST macro family was removed. Add back what we were using in gcc-common.h, as they are simple wrappers. See GCC commits: c3d96ff9e916c02584aa081f03ab999292efbb50 458c7926d48959abcb2c1adaa22458e27459a551 Suggested-by: Ingo Saitz Link: https://lore.kernel.org/lkml/ab6OKoay0OWkywjK@spatz.zoo Fixes: 6b90bd4ba40b ("GCC plugin infrastructure") Tested-by: Ivan Bulatovic Tested-by: Christopher Cradock Signed-off-by: Kees Cook Signed-off-by: Sasha Levin --- scripts/gcc-plugins/gcc-common.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h index 8f1b3500f8e2..abb1964c44d4 100644 --- a/scripts/gcc-plugins/gcc-common.h +++ b/scripts/gcc-plugins/gcc-common.h @@ -309,7 +309,9 @@ typedef const gimple *const_gimple_ptr; #define gimple gimple_ptr #define const_gimple const_gimple_ptr #undef CONST_CAST_GIMPLE -#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X)) +#define CONST_CAST_GIMPLE(X) const_cast((X)) +#undef CONST_CAST_TREE +#define CONST_CAST_TREE(X) const_cast((X)) /* gimple related */ static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL) From f7808b7ddcf21486776cf6e39902e4de7446e7a1 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Tue, 5 May 2026 12:24:17 +0200 Subject: [PATCH 287/697] x86/xen: Fix xen_e820_swap_entry_with_ram() [ Upstream commit 28e03f78e69cf6628b81f24777799778528a84c1 ] When swapping a not page-aligned E820 map entry with RAM, the start address of the modified entry is calculated wrong (the offset into the page is subtracted instead of being added to the page address). Fixes: be35d91c8880 ("xen: tolerate ACPI NVS memory overlapping with Xen allocated memory") Reported-by: Jan Beulich Reviewed-by: Jan Beulich Signed-off-by: Juergen Gross Message-ID: <20260505102417.208138-1-jgross@suse.com> Signed-off-by: Sasha Levin --- arch/x86/xen/setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 3823e52aef52..6260f65a78c5 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -655,7 +655,7 @@ static void __init xen_e820_swap_entry_with_ram(struct e820_entry *swap_entry) /* Fill new entry (keep size and page offset). */ entry->type = swap_entry->type; entry->addr = entry_end - swap_size + - swap_addr - swap_entry->addr; + swap_entry->addr - swap_addr; entry->size = swap_entry->size; /* Convert old entry to RAM, align to pages. */ From 1370acb8bc390de37ea50d9c414eba3cc0b90f9f Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 13 May 2026 15:26:10 +0200 Subject: [PATCH 288/697] ovpn: disable BHs when updating device stats [ Upstream commit 0c0dddc07d272a8d25922e48041e8e4d2434df7e ] ovpn updates dev->dstats from both process and softirq contexts. In particular, TCP paths may run from socket callbacks, workqueues or strparser work, while UDP receive and ovpn's ndo_start_xmit path may update the same per-device dstats from BH context. Add ovpn device drop-stat helpers that disable BHs around dev_dstats_rx_dropped() and dev_dstats_tx_dropped(), and use them for drop accounting. The successful RX dev_dstats_rx_add() update is already covered by the BH-disabled section around gro_cells_receive(). For the successful TCP TX dev_dstats_tx_add() update, replace the existing preempt-disabled section with a BH-disabled one. Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli Signed-off-by: Sasha Levin --- drivers/net/ovpn/io.c | 12 ++++++------ drivers/net/ovpn/stats.h | 16 ++++++++++++++++ drivers/net/ovpn/tcp.c | 10 +++++----- drivers/net/ovpn/udp.c | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/net/ovpn/io.c b/drivers/net/ovpn/io.c index 955c9a37e1f8..c03e58e28a86 100644 --- a/drivers/net/ovpn/io.c +++ b/drivers/net/ovpn/io.c @@ -196,7 +196,7 @@ void ovpn_decrypt_post(void *data, int ret) skb = NULL; drop: if (unlikely(skb)) - dev_dstats_rx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); kfree_skb(skb); drop_nocount: if (likely(peer)) @@ -220,7 +220,7 @@ void ovpn_recv(struct ovpn_peer *peer, struct sk_buff *skb) net_info_ratelimited("%s: no available key for peer %u, key-id: %u\n", netdev_name(peer->ovpn->dev), peer->id, key_id); - dev_dstats_rx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); kfree_skb(skb); ovpn_peer_put(peer); return; @@ -298,7 +298,7 @@ void ovpn_encrypt_post(void *data, int ret) rcu_read_unlock(); err: if (unlikely(skb)) - dev_dstats_tx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); if (likely(peer)) ovpn_peer_put(peer); if (likely(ks)) @@ -340,7 +340,7 @@ static void ovpn_send(struct ovpn_priv *ovpn, struct sk_buff *skb, */ skb_list_walk_safe(skb, curr, next) { if (unlikely(!ovpn_encrypt_one(peer, curr))) { - dev_dstats_tx_dropped(ovpn->dev); + ovpn_dev_dstats_tx_dropped(ovpn->dev); kfree_skb(curr); } } @@ -411,7 +411,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(!curr)) { net_err_ratelimited("%s: skb_share_check failed for payload packet\n", netdev_name(dev)); - dev_dstats_tx_dropped(ovpn->dev); + ovpn_dev_dstats_tx_dropped(ovpn->dev); continue; } @@ -437,7 +437,7 @@ netdev_tx_t ovpn_net_xmit(struct sk_buff *skb, struct net_device *dev) drop: ovpn_peer_put(peer); drop_no_peer: - dev_dstats_tx_dropped(ovpn->dev); + ovpn_dev_dstats_tx_dropped(ovpn->dev); skb_tx_error(skb); kfree_skb_list(skb); return NETDEV_TX_OK; diff --git a/drivers/net/ovpn/stats.h b/drivers/net/ovpn/stats.h index 53433d8b6c33..3a45b97c0056 100644 --- a/drivers/net/ovpn/stats.h +++ b/drivers/net/ovpn/stats.h @@ -11,6 +11,8 @@ #ifndef _NET_OVPN_OVPNSTATS_H_ #define _NET_OVPN_OVPNSTATS_H_ +#include + /* one stat */ struct ovpn_peer_stat { atomic64_t bytes; @@ -44,4 +46,18 @@ static inline void ovpn_peer_stats_increment_tx(struct ovpn_peer_stats *stats, ovpn_peer_stats_increment(&stats->tx, n); } +static inline void ovpn_dev_dstats_tx_dropped(struct net_device *dev) +{ + local_bh_disable(); + dev_dstats_tx_dropped(dev); + local_bh_enable(); +} + +static inline void ovpn_dev_dstats_rx_dropped(struct net_device *dev) +{ + local_bh_disable(); + dev_dstats_rx_dropped(dev); + local_bh_enable(); +} + #endif /* _NET_OVPN_OVPNSTATS_H_ */ diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c index 5f345ae7d59d..505c2f214c9f 100644 --- a/drivers/net/ovpn/tcp.c +++ b/drivers/net/ovpn/tcp.c @@ -152,7 +152,7 @@ static void ovpn_tcp_rcv(struct strparser *strp, struct sk_buff *skb) if (WARN_ON(!ovpn_peer_hold(peer))) goto err_nopeer; schedule_work(&peer->tcp.defer_del_work); - dev_dstats_rx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_rx_dropped(peer->ovpn->dev); err_nopeer: kfree_skb(skb); } @@ -298,9 +298,9 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk) } while (peer->tcp.out_msg.len > 0); if (!peer->tcp.out_msg.len) { - preempt_disable(); + local_bh_disable(); dev_dstats_tx_add(peer->ovpn->dev, skb->len); - preempt_enable(); + local_bh_enable(); } kfree_skb(peer->tcp.out_msg.skb); @@ -331,7 +331,7 @@ static void ovpn_tcp_send_sock_skb(struct ovpn_peer *peer, struct sock *sk, ovpn_tcp_send_sock(peer, sk); if (peer->tcp.out_msg.skb) { - dev_dstats_tx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); kfree_skb(skb); return; } @@ -353,7 +353,7 @@ void ovpn_tcp_send_skb(struct ovpn_peer *peer, struct sock *sk, if (sock_owned_by_user(sk)) { if (skb_queue_len(&peer->tcp.out_queue) >= READ_ONCE(net_hotdata.max_backlog)) { - dev_dstats_tx_dropped(peer->ovpn->dev); + ovpn_dev_dstats_tx_dropped(peer->ovpn->dev); kfree_skb(skb); goto unlock; } diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c index 272b535ecaad..367563d84472 100644 --- a/drivers/net/ovpn/udp.c +++ b/drivers/net/ovpn/udp.c @@ -126,7 +126,7 @@ static int ovpn_udp_encap_recv(struct sock *sk, struct sk_buff *skb) return 0; drop: - dev_dstats_rx_dropped(ovpn->dev); + ovpn_dev_dstats_rx_dropped(ovpn->dev); drop_noovpn: kfree_skb(skb); return 0; From 81c8a9f75a4268f2b4756107b298c69be9248c0d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 13 May 2026 08:58:25 -0400 Subject: [PATCH 289/697] tls: Preserve sk_err across recvmsg() when data has been copied [ Upstream commit f508262ae9f21fe0e6c0749948b9dc7dd5a62a70 ] The sk_err check in tls_rx_rec_wait() consumes the error via sock_error(), which clears sk_err atomically. When the caller (tls_sw_recvmsg, tls_sw_splice_read, or tls_sw_read_sock) already has bytes copied to userspace, it returns those bytes and discards the error from this call. sk_err is now zero on the socket, so the next read syscall observes only RCV_SHUTDOWN and reports a clean EOF instead of the actual error (typically -ECONNRESET). The race is reachable when tls_read_flush_backlog()'s periodic sk_flush_backlog() triggers tcp_reset() in the middle of a multi-record read. Pass a has_copied flag to tls_rx_rec_wait(). When has_copied is false, consume sk_err via sock_error() as before. When has_copied is true, report the error from READ_ONCE() but leave sk_err set: the caller returns the byte count and discards the err from this call, and the next read syscall surfaces the preserved sk_err. This mirrors the tcp_recvmsg() preserve-and-surface pattern. The decrypt-abort path is unaffected: tls_err_abort() raises sk_err to EBADMSG after tls_rx_rec_wait() returns, and nothing on the caller's return path consumes it, so the EBADMSG surfaces on the next read. tls_sw_splice_read() passes has_copied=false: it processes one record per call, so no bytes have been copied within the function when tls_rx_rec_wait() runs. A reset that arrives between iterations of splice_direct_to_actor() (the sendfile() path) is still consumed by sock_error() in the later call, and the outer loop returns the prior iterations' byte count and drops the error. tcp_splice_read() exhibits the same pattern at the iteration boundary; addressing it belongs at the splice_direct_to_actor() layer and is out of scope here. Fixes: c46b01839f7a ("tls: rx: periodically flush socket backlog") Suggested-by: Jakub Kicinski Signed-off-by: Chuck Lever Link: https://patch.msgid.link/20260513125825.205189-1-cel@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/tls/tls_sw.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index b28eb04075d1..034f322054e5 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1366,9 +1366,14 @@ void tls_sw_splice_eof(struct socket *sock) mutex_unlock(&tls_ctx->tx_lock); } +/* When has_copied is true the caller has already moved bytes to + * userspace. Report sk_err but leave it set so the next read + * surfaces it instead of a spurious EOF, otherwise sk_err is + * consumed via sock_error(). + */ static int tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, - bool released) + bool released, bool has_copied) { struct tls_context *tls_ctx = tls_get_ctx(sk); struct tls_sw_context_rx *ctx = tls_sw_ctx_rx(tls_ctx); @@ -1386,8 +1391,11 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, if (!sk_psock_queue_empty(psock)) return 0; - if (sk->sk_err) + if (sk->sk_err) { + if (has_copied) + return -READ_ONCE(sk->sk_err); return sock_error(sk); + } if (ret < 0) return ret; @@ -1423,7 +1431,7 @@ tls_rx_rec_wait(struct sock *sk, struct sk_psock *psock, bool nonblock, } if (unlikely(!tls_strp_msg_load(&ctx->strp, released))) - return tls_rx_rec_wait(sk, psock, nonblock, false); + return tls_rx_rec_wait(sk, psock, nonblock, false, has_copied); return 1; } @@ -2111,7 +2119,7 @@ int tls_sw_recvmsg(struct sock *sk, int to_decrypt, chunk; err = tls_rx_rec_wait(sk, psock, flags & MSG_DONTWAIT, - released); + released, !!(decrypted + copied)); if (err <= 0) { if (psock) { chunk = sk_msg_recvmsg(sk, psock, msg, len, @@ -2298,7 +2306,7 @@ ssize_t tls_sw_splice_read(struct socket *sock, loff_t *ppos, struct tls_decrypt_arg darg; err = tls_rx_rec_wait(sk, NULL, flags & SPLICE_F_NONBLOCK, - true); + true, false); if (err <= 0) goto splice_read_end; @@ -2384,7 +2392,7 @@ int tls_sw_read_sock(struct sock *sk, read_descriptor_t *desc, } else { struct tls_decrypt_arg darg; - err = tls_rx_rec_wait(sk, NULL, true, released); + err = tls_rx_rec_wait(sk, NULL, true, released, !!copied); if (err <= 0) goto read_sock_end; From b4dc0056397fe8c94cd440ae219ae2b68b875625 Mon Sep 17 00:00:00 2001 From: Jeroen Massar Date: Wed, 13 May 2026 09:33:02 +0300 Subject: [PATCH 290/697] net/mlx5: Do not restore destination-less TC rules [ Upstream commit 8d0a5af8b1ba598e7340761729801624e7a9330e ] After IPsec policy/state TX rules are added, any TC flow rule, which forwards packets to uplink, is modified to forward to IPsec TX tables. As these tables are destroyed dynamically, whenever there is no reference to them, the destinations of this kind of rules must be restored to uplink, unless there is no destination for that rule. The flow rules FLOW_ACTION_ACCEPT, DROP, TRAP, GOTO and SAMPLE do not have a destination port, and thus out_count = 0. At cleanup time of the rules in mlx5_esw_ipsec_modify_flow_dests we call mlx5_eswitch_restore_ipsec_rule but as the above types do not have a destination we get an underflow of out_count, as the port is passed, which is esw_attr->out_count - 1. This change avoids calling mlx5_eswitch_restore_ipsec_rule when there are no output destinations and thus avoids the underflow. Fixes: d1569537a837 ("net/mlx5e: Modify and restore TC rules for IPSec TX rules") Signed-off-by: Jeroen Massar Reviewed-by: Jianbo Liu Reviewed-by: Cosmin Ratiu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260513063302.333761-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c index 3cfe743610d3..ab50d2c734ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/ipsec_fs.c @@ -142,7 +142,8 @@ static int mlx5_esw_ipsec_modify_flow_dests(struct mlx5_eswitch *esw, attr = flow->attr; esw_attr = attr->esw_attr; - if (esw_attr->out_count - esw_attr->split_count > 1) + if (!esw_attr->out_count || + esw_attr->out_count - esw_attr->split_count > 1) return 0; err = mlx5_eswitch_restore_ipsec_rule(esw, flow->rule[0], esw_attr, From 36de63965464026ebc520c30d4634ae8be513212 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Mon, 11 May 2026 12:53:17 -0500 Subject: [PATCH 291/697] scsi: sd: Fix return code handling in sd_spinup_disk() [ Upstream commit 6ea68a8dc7d2711504d944811981a5304af7d7a9 ] As found by smatch-ci, scsi_execute_cmd() can return negative or positve values so we should use a int instead of unsigned int. Fixes: b4d0c33a32c3 ("scsi: sd: Fix sshdr use in sd_spinup_disk") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-scsi/agFbI7E6JQwd3wGW@stanley.mountain/T/#u Signed-off-by: Mike Christie Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260511175317.114007-1-michael.christie@oracle.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/sd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 072d4c4add33..d9bae23cb929 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2398,8 +2398,7 @@ sd_spinup_disk(struct scsi_disk *sdkp) { static const u8 cmd[10] = { TEST_UNIT_READY }; unsigned long spintime_expire = 0; - int spintime, sense_valid = 0; - unsigned int the_result; + int the_result, spintime, sense_valid = 0; struct scsi_sense_hdr sshdr; struct scsi_failure failure_defs[] = { /* Do not retry Medium Not Present */ From 1ddf678bb75b6383c775ece61d40956c441d8a26 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 13 May 2026 21:08:52 +0200 Subject: [PATCH 292/697] ASoC: codecs: fs210x: fix possible buffer overflow [ Upstream commit 0d435a7ebcd4e97e47673c1ab6fb27f973a053ec ] In fs210x_effect_scene_info(), a string was copied like this: strscpy(DST, SRC, strlen(SRC) + 1); A buffer overflow would happen if strlen(SRC) >= sizeof(DST). Actually, strscpy() must be used this way: strscpy(DST, SRC, sizeof(DST)); strscpy(DST, SRC); // defaults to sizeof(DST) Fixes: 756117701779 ("ASoC: codecs: Add FourSemi FS2104/5S audio amplifier driver") Signed-off-by: Alexander A. Klimov Link: https://patch.msgid.link/20260513190852.196723-2-grandmaster@al2klimov.de Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/fs210x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index e2f85714972d..e2207c53c50d 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -968,7 +968,7 @@ static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol, if (scene->name) name = scene->name; - strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1); + strscpy(uinfo->value.enumerated.name, name); return 0; } From c53cac053d625b3e54bce39b996a2434bb62801f Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Fri, 8 May 2026 10:39:14 +0700 Subject: [PATCH 293/697] ALSA: scarlett2: Add missing error check when initialise Autogain Status [ Upstream commit c0e4fffc0f474b7ed10adee4ab2bc1a66d36fc72 ] When initialise new control with scarlett2_add_new_ctl() function for Autogain Status, scarlett2_add_new_ctl() might throw an error. So, add error check after initialise new control for Autogain Status. This is reported by Coverity Scan with CID 1598781 as UNUSED_VALUE. Fixes: 0a995e38dc44 ("ALSA: scarlett2: Add support for software-controllable input gain") Signed-off-by: Robertus Diawan Chris Link: https://patch.msgid.link/20260508033914.111596-1-robertusdchris@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/usb/mixer_scarlett2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index d0cad1d1efa3..da3c2741ef57 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -6707,6 +6707,8 @@ static int scarlett2_add_line_in_ctls(struct usb_mixer_interface *mixer) err = scarlett2_add_new_ctl( mixer, &scarlett2_autogain_status_ctl, i, 1, s, &private->autogain_status_ctls[i]); + if (err < 0) + return err; } /* Add autogain target controls */ From 782693eb53f8111bd736026448847faa01b8c30b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 15 May 2026 10:19:09 -0600 Subject: [PATCH 294/697] io_uring/net: punt IORING_OP_BIND async if it needs file create [ Upstream commit ccd25890f73c082fe2657ed227b497d6ac5fdc40 ] For two reasons: 1) An opcode cannot block inside io_uring_enter() doing submissions, as it'll stall the submission side pipeline. 2) Ending up in sb_start_write() -> __sb_start_write() -> percpu_down_read_freezable() introduces a new lockdep edge, which it correctly complains about. Check if the socket type is AF_UNIX and has a non-empty pathname. If it does, mark it REQ_F_FORCE_ASYNC to punt the submission to io-wq rather than attempt to do it inline. Fixes: 7481fd93fa0a ("io_uring: Introduce IORING_OP_BIND") Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- io_uring/net.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/io_uring/net.c b/io_uring/net.c index ad08f693bccb..7595850c2217 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -1837,11 +1838,29 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags) return IOU_COMPLETE; } +/* + * Check if bind request would potentially end up with filename_create(), + * which in turn end up in mnt_want_write() which will grab the fs + * percpu start write sem. This can trigger a lockdep warning. + */ +static int io_bind_file_create(const struct io_async_msghdr *io, int addr_len) +{ + const struct sockaddr_un *sun; + + if (io->addr.ss_family != AF_UNIX) + return 0; + if (addr_len <= offsetof(struct sockaddr_un, sun_path)) + return 0; + sun = (const struct sockaddr_un *) &io->addr; + return sun->sun_path[0] != '\0'; +} + int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { struct io_bind *bind = io_kiocb_to_cmd(req, struct io_bind); struct sockaddr __user *uaddr; struct io_async_msghdr *io; + int ret; if (sqe->len || sqe->buf_index || sqe->rw_flags || sqe->splice_fd_in) return -EINVAL; @@ -1852,7 +1871,12 @@ int io_bind_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) io = io_msg_alloc_async(req); if (unlikely(!io)) return -ENOMEM; - return move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); + ret = move_addr_to_kernel(uaddr, bind->addr_len, &io->addr); + if (unlikely(ret)) + return ret; + if (io_bind_file_create(io, bind->addr_len)) + req->flags |= REQ_F_FORCE_ASYNC; + return 0; } int io_bind(struct io_kiocb *req, unsigned int issue_flags) From 76b995bc57bd90cb6e954e1966fbd8786da47f0d Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Thu, 14 May 2026 11:29:48 +0200 Subject: [PATCH 295/697] vsock/virtio: fix zerocopy completion for multi-skb sends [ Upstream commit ae38d9179190a956e2a87a69ef1dd6f451b51c4d ] When a large message is fragmented into multiple skbs, the zerocopy uarg is only allocated and attached to the last skb in the loop. Non-final skbs carry pinned user pages with no completion tracking, so the kernel has no way to notify userspace when those pages are safe to reuse. If the loop breaks early the uarg is never allocated at all, leaking pinned pages with no completion notification. Fix this by following the approach used by TCP: allocate the zerocopy uarg (if not provided by the caller) before the send loop and attach it to every skb via skb_zcopy_set(), which takes a reference per skb. Each skb's completion properly decrements the refcount, and the notification only fires after the last skb is freed. On failure, if no data was sent, the uarg is cleanly aborted via net_zcopy_put_abort(). This issue was initially discovered by sashiko while reviewing commit 1cb36e252211 ("vsock/virtio: fix MSG_ZEROCOPY pinned-pages accounting") but was pre-existing. Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support") Closes: https://sashiko.dev/#/patchset/20260420132051.217589-1-sgarzare%40redhat.com Reported-by: Maher Azzouzi Signed-off-by: Stefano Garzarella Acked-by: Michael S. Tsirkin Acked-by: Arseniy Krasnov Link: https://patch.msgid.link/20260514092948.268720-1-sgarzare@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/vmw_vsock/virtio_transport_common.c | 83 ++++++++++--------------- 1 file changed, 34 insertions(+), 49 deletions(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 65e2b2489234..ed42e08798a9 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -72,34 +72,6 @@ static bool virtio_transport_can_zcopy(const struct virtio_transport *t_ops, return true; } -static int virtio_transport_init_zcopy_skb(struct vsock_sock *vsk, - struct sk_buff *skb, - struct msghdr *msg, - size_t pkt_len, - bool zerocopy) -{ - struct ubuf_info *uarg; - - if (msg->msg_ubuf) { - uarg = msg->msg_ubuf; - net_zcopy_get(uarg); - } else { - struct ubuf_info_msgzc *uarg_zc; - - uarg = msg_zerocopy_realloc(sk_vsock(vsk), - pkt_len, NULL, false); - if (!uarg) - return -1; - - uarg_zc = uarg_to_msgzc(uarg); - uarg_zc->zerocopy = zerocopy ? 1 : 0; - } - - skb_zcopy_init(skb, uarg); - - return 0; -} - static int virtio_transport_fill_skb(struct sk_buff *skb, struct virtio_vsock_pkt_info *info, size_t len, @@ -319,8 +291,10 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, u32 src_cid, src_port, dst_cid, dst_port; const struct virtio_transport *t_ops; struct virtio_vsock_sock *vvs; + struct ubuf_info *uarg = NULL; u32 pkt_len = info->pkt_len; bool can_zcopy = false; + bool have_uref = false; u32 rest_len; int ret; @@ -362,6 +336,25 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, if (can_zcopy) max_skb_len = min_t(u32, VIRTIO_VSOCK_MAX_PKT_BUF_SIZE, (MAX_SKB_FRAGS * PAGE_SIZE)); + + if (info->msg->msg_flags & MSG_ZEROCOPY && + info->op == VIRTIO_VSOCK_OP_RW) { + uarg = info->msg->msg_ubuf; + + if (!uarg) { + uarg = msg_zerocopy_realloc(sk_vsock(vsk), + pkt_len, NULL, false); + if (!uarg) { + virtio_transport_put_credit(vvs, pkt_len); + return -ENOMEM; + } + + if (!can_zcopy) + uarg_to_msgzc(uarg)->zerocopy = 0; + + have_uref = true; + } + } } rest_len = pkt_len; @@ -380,27 +373,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, break; } - /* We process buffer part by part, allocating skb on - * each iteration. If this is last skb for this buffer - * and MSG_ZEROCOPY mode is in use - we must allocate - * completion for the current syscall. - * - * Pass pkt_len because msg iter is already consumed - * by virtio_transport_fill_skb(), so iter->count - * can not be used for RLIMIT_MEMLOCK pinned-pages - * accounting done by msg_zerocopy_realloc(). - */ - if (info->msg && info->msg->msg_flags & MSG_ZEROCOPY && - skb_len == rest_len && info->op == VIRTIO_VSOCK_OP_RW) { - if (virtio_transport_init_zcopy_skb(vsk, skb, - info->msg, - pkt_len, - can_zcopy)) { - kfree_skb(skb); - ret = -ENOMEM; - break; - } - } + skb_zcopy_set(skb, uarg, NULL); virtio_transport_inc_tx_pkt(vvs, skb); @@ -424,6 +397,18 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, virtio_transport_put_credit(vvs, rest_len); + /* msg_zerocopy_realloc() initializes the ubuf_info refcnt to 1. + * skb_zcopy_set() increases it for each skb, so we can drop that + * initial reference to keep it balanced. + */ + if (have_uref) { + if (rest_len == pkt_len) + /* No data sent, abort the notification. */ + net_zcopy_put_abort(uarg, true); + else + net_zcopy_put(uarg); + } + /* Return number of bytes, if any data has been sent. */ if (rest_len != pkt_len) ret = pkt_len - rest_len; From 35f69e993d002b22b108b1785c5a2b2c0a14acde Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 15 Oct 2025 12:40:06 +0100 Subject: [PATCH 296/697] btrfs: add macros to facilitate printing of keys [ Upstream commit 95de4b097e25225d4deb5a33a4bfc27bb441f2d8 ] There's a lot of places where we need to print a key, and it's tiresome to type the format specifier, typically "(%llu %u %llu)", as well as passing 3 arguments to a prink family function (key->objectid, key->type, key->offset). So add a couple macros for this just like we have for csum values in btrfs_inode.h (CSUM_FMT and CSUM_FMT_VALUE). This also ensures that we consistently print a key in the same format, always as "(%llu %llu %llu)", which is the most common format we use, but we have a few variations such as "[%llu %llu %llu]" for no good reason. This patch introduces the macros while the next one makes use of it. This is to ease backports of future patches, since then we can backport this patch which is simple and short and then backport those future patches, as the next patch in the series that makes use of these new macros is quite large and may have some dependencies. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/fs.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 37aa8d141a83..acdb9e52f6fd 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -73,6 +73,9 @@ struct btrfs_space_info; #define BTRFS_SUPER_INFO_SIZE 4096 static_assert(sizeof(struct btrfs_super_block) == BTRFS_SUPER_INFO_SIZE); +#define BTRFS_KEY_FMT "(%llu %u %llu)" +#define BTRFS_KEY_FMT_VALUE(key) (key)->objectid, (key)->type, (key)->offset + /* * Number of metadata items necessary for an unlink operation: * From 16141bef6fb1a72bb11a5e46965facc9846c9d42 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Wed, 15 Oct 2025 13:16:26 +0100 Subject: [PATCH 297/697] btrfs: use the key format macros when printing keys [ Upstream commit af1e800c0244a04f5eb0993745c23d974f262628 ] Change all locations that print a key to use the new macros to print them in order to ensure a consistent style and avoid repetitive code. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/backref.c | 11 +++++------ fs/btrfs/ctree.c | 17 +++++++---------- fs/btrfs/extent-tree.c | 14 +++++++------- fs/btrfs/inode.c | 4 ++-- fs/btrfs/print-tree.c | 14 ++++++-------- fs/btrfs/qgroup.c | 6 ++---- fs/btrfs/relocation.c | 4 ++-- fs/btrfs/root-tree.c | 4 ++-- fs/btrfs/send.c | 10 ++++------ fs/btrfs/tree-checker.c | 21 +++++++++------------ fs/btrfs/tree-log.c | 38 ++++++++++++++++++-------------------- 11 files changed, 64 insertions(+), 79 deletions(-) diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 2ab550a1e715..e050d0938dc4 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -666,10 +666,9 @@ static int resolve_indirect_ref(struct btrfs_backref_walk_ctx *ctx, ret = btrfs_search_old_slot(root, &search_key, path, ctx->time_seq); btrfs_debug(ctx->fs_info, - "search slot in root %llu (level %d, ref count %d) returned %d for key (%llu %u %llu)", - ref->root_id, level, ref->count, ret, - ref->key_for_search.objectid, ref->key_for_search.type, - ref->key_for_search.offset); +"search slot in root %llu (level %d, ref count %d) returned %d for key " BTRFS_KEY_FMT, + ref->root_id, level, ref->count, ret, + BTRFS_KEY_FMT_VALUE(&ref->key_for_search)); if (ret < 0) goto out; @@ -3323,9 +3322,9 @@ static int handle_indirect_tree_backref(struct btrfs_trans_handle *trans, eb = path->nodes[level]; if (btrfs_node_blockptr(eb, path->slots[level]) != cur->bytenr) { btrfs_err(fs_info, -"couldn't find block (%llu) (level %d) in tree (%llu) with key (%llu %u %llu)", +"couldn't find block (%llu) (level %d) in tree (%llu) with key " BTRFS_KEY_FMT, cur->bytenr, level - 1, btrfs_root_id(root), - tree_key->objectid, tree_key->type, tree_key->offset); + BTRFS_KEY_FMT_VALUE(tree_key)); btrfs_put_root(root); ret = -ENOENT; goto out; diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 6e053caa6e10..27e2adc2ee71 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2599,12 +2599,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, if (unlikely(btrfs_comp_keys(&disk_key, new_key) >= 0)) { btrfs_print_leaf(eb); btrfs_crit(fs_info, - "slot %u key (%llu %u %llu) new key (%llu %u %llu)", + "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT, slot, btrfs_disk_key_objectid(&disk_key), btrfs_disk_key_type(&disk_key), btrfs_disk_key_offset(&disk_key), - new_key->objectid, new_key->type, - new_key->offset); + BTRFS_KEY_FMT_VALUE(new_key)); BUG(); } } @@ -2613,12 +2612,11 @@ void btrfs_set_item_key_safe(struct btrfs_trans_handle *trans, if (unlikely(btrfs_comp_keys(&disk_key, new_key) <= 0)) { btrfs_print_leaf(eb); btrfs_crit(fs_info, - "slot %u key (%llu %u %llu) new key (%llu %u %llu)", + "slot %u key " BTRFS_KEY_FMT " new key " BTRFS_KEY_FMT, slot, btrfs_disk_key_objectid(&disk_key), btrfs_disk_key_type(&disk_key), btrfs_disk_key_offset(&disk_key), - new_key->objectid, new_key->type, - new_key->offset); + BTRFS_KEY_FMT_VALUE(new_key)); BUG(); } } @@ -2677,10 +2675,9 @@ static bool check_sibling_keys(const struct extent_buffer *left, btrfs_crit(left->fs_info, "right extent buffer:"); btrfs_print_tree(right, false); btrfs_crit(left->fs_info, -"bad key order, sibling blocks, left last (%llu %u %llu) right first (%llu %u %llu)", - left_last.objectid, left_last.type, - left_last.offset, right_first.objectid, - right_first.type, right_first.offset); +"bad key order, sibling blocks, left last " BTRFS_KEY_FMT " right first " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&left_last), + BTRFS_KEY_FMT_VALUE(&right_first)); return true; } return false; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 863b45092a19..e40835fe4bde 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -165,8 +165,8 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans, if (unlikely(num_refs == 0)) { ret = -EUCLEAN; btrfs_err(fs_info, - "unexpected zero reference count for extent item (%llu %u %llu)", - key.objectid, key.type, key.offset); + "unexpected zero reference count for extent item " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&key)); btrfs_abort_transaction(trans, ret); return ret; } @@ -597,8 +597,8 @@ static noinline int remove_extent_data_ref(struct btrfs_trans_handle *trans, num_refs = btrfs_shared_data_ref_count(leaf, ref2); } else { btrfs_err(trans->fs_info, - "unrecognized backref key (%llu %u %llu)", - key.objectid, key.type, key.offset); + "unrecognized backref key " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&key)); btrfs_abort_transaction(trans, -EUCLEAN); return -EUCLEAN; } @@ -3324,9 +3324,9 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, if (iref) { if (unlikely(path->slots[0] != extent_slot)) { abort_and_dump(trans, path, -"invalid iref, extent item key (%llu %u %llu) slot %u doesn't have wanted iref", - key.objectid, key.type, - key.offset, path->slots[0]); +"invalid iref, extent item key " BTRFS_KEY_FMT " slot %u doesn't have wanted iref", + BTRFS_KEY_FMT_VALUE(&key), + path->slots[0]); ret = -EUCLEAN; goto out; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2c361e0691fc..dcc0e53782c6 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -5668,9 +5668,9 @@ static int btrfs_inode_by_name(struct btrfs_inode *dir, struct dentry *dentry, location->type != BTRFS_ROOT_ITEM_KEY)) { ret = -EUCLEAN; btrfs_warn(root->fs_info, -"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location(%llu %u %llu))", +"%s gets something invalid in DIR_ITEM (name %s, directory ino %llu, location " BTRFS_KEY_FMT ")", __func__, fname.disk_name.name, btrfs_ino(dir), - location->objectid, location->type, location->offset); + BTRFS_KEY_FMT_VALUE(location)); } if (!ret) *type = btrfs_dir_ftype(path->nodes[0], di); diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 62b993fae54f..06edc5cdb00d 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -131,7 +131,7 @@ static void print_extent_item(const struct extent_buffer *eb, int slot, int type struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)(ei + 1); btrfs_tree_block_key(eb, info, &key); - pr_info("\t\ttree block key (%llu %u %llu) level %d\n", + pr_info("\t\ttree block key " BTRFS_KEY_FMT " level %d\n", btrfs_disk_key_objectid(&key), key.type, btrfs_disk_key_offset(&key), btrfs_tree_block_level(eb, info)); @@ -277,9 +277,8 @@ static void print_dir_item(const struct extent_buffer *eb, int i) struct btrfs_key location; btrfs_dir_item_key_to_cpu(eb, di, &location); - pr_info("\t\tlocation key (%llu %u %llu) type %d\n", - location.objectid, location.type, location.offset, - btrfs_dir_ftype(eb, di)); + pr_info("\t\tlocation key " BTRFS_KEY_FMT " type %d\n", + BTRFS_KEY_FMT_VALUE(&location), btrfs_dir_ftype(eb, di)); pr_info("\t\ttransid %llu data_len %u name_len %u\n", btrfs_dir_transid(eb, di), data_len, name_len); di = (struct btrfs_dir_item *)((char *)di + len); @@ -598,10 +597,9 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow) print_eb_refs_lock(c); for (i = 0; i < nr; i++) { btrfs_node_key_to_cpu(c, &key, i); - pr_info("\tkey %d (%llu %u %llu) block %llu gen %llu\n", - i, key.objectid, key.type, key.offset, - btrfs_node_blockptr(c, i), - btrfs_node_ptr_generation(c, i)); + pr_info("\tkey %d " BTRFS_KEY_FMT " block %llu gen %llu\n", + i, BTRFS_KEY_FMT_VALUE(&key), btrfs_node_blockptr(c, i), + btrfs_node_ptr_generation(c, i)); } if (!follow) return; diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 302bb3ecf39a..1f83af14568c 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -3734,10 +3734,8 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, path, 1, 0); btrfs_debug(fs_info, - "current progress key (%llu %u %llu), search_slot ret %d", - fs_info->qgroup_rescan_progress.objectid, - fs_info->qgroup_rescan_progress.type, - fs_info->qgroup_rescan_progress.offset, ret); + "current progress key " BTRFS_KEY_FMT ", search_slot ret %d", + BTRFS_KEY_FMT_VALUE(&fs_info->qgroup_rescan_progress), ret); if (ret) { /* diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0765e06d00b8..fc76013b1a3e 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -615,8 +615,8 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans, btrfs_disk_key_to_cpu(&cpu_key, &root->root_item.drop_progress); btrfs_err(fs_info, - "cannot relocate partially dropped subvolume %llu, drop progress key (%llu %u %llu)", - objectid, cpu_key.objectid, cpu_key.type, cpu_key.offset); + "cannot relocate partially dropped subvolume %llu, drop progress key " BTRFS_KEY_FMT, + objectid, BTRFS_KEY_FMT_VALUE(&cpu_key)); ret = -EUCLEAN; goto fail; } diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index d07eab70f759..6a7e297ab0a7 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -147,8 +147,8 @@ int btrfs_update_root(struct btrfs_trans_handle *trans, struct btrfs_root if (unlikely(ret > 0)) { btrfs_crit(fs_info, - "unable to find root key (%llu %u %llu) in tree %llu", - key->objectid, key->type, key->offset, btrfs_root_id(root)); + "unable to find root key " BTRFS_KEY_FMT " in tree %llu", + BTRFS_KEY_FMT_VALUE(key), btrfs_root_id(root)); ret = -EUCLEAN; btrfs_abort_transaction(trans, ret); return ret; diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 9012ce7a742f..04473387ee8b 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c @@ -1053,10 +1053,8 @@ static int iterate_inode_ref(struct btrfs_root *root, struct btrfs_path *path, } if (unlikely(start < p->buf)) { btrfs_err(root->fs_info, - "send: path ref buffer underflow for key (%llu %u %llu)", - found_key->objectid, - found_key->type, - found_key->offset); + "send: path ref buffer underflow for key " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(found_key)); ret = -EINVAL; goto out; } @@ -7276,8 +7274,8 @@ static int search_key_again(const struct send_ctx *sctx, if (unlikely(ret > 0)) { btrfs_print_tree(path->nodes[path->lowest_level], false); btrfs_err(root->fs_info, -"send: key (%llu %u %llu) not found in %s root %llu, lowest_level %d, slot %d", - key->objectid, key->type, key->offset, +"send: key " BTRFS_KEY_FMT" not found in %s root %llu, lowest_level %d, slot %d", + BTRFS_KEY_FMT_VALUE(key), (root == sctx->parent_root ? "parent" : "send"), btrfs_root_id(root), path->lowest_level, path->slots[path->lowest_level]); diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 33a45737c4cf..db7402836340 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1635,10 +1635,9 @@ static int check_extent_item(struct extent_buffer *leaf, if (unlikely(prev_end > key->objectid)) { extent_err(leaf, slot, - "previous extent [%llu %u %llu] overlaps current extent [%llu %u %llu]", - prev_key->objectid, prev_key->type, - prev_key->offset, key->objectid, key->type, - key->offset); + "previous extent " BTRFS_KEY_FMT " overlaps current extent " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(prev_key), + BTRFS_KEY_FMT_VALUE(key)); return -EUCLEAN; } } @@ -2077,10 +2076,9 @@ enum btrfs_tree_block_status __btrfs_check_leaf(struct extent_buffer *leaf) /* Make sure the keys are in the right order */ if (unlikely(btrfs_comp_cpu_keys(&prev_key, &key) >= 0)) { generic_err(leaf, slot, - "bad key order, prev (%llu %u %llu) current (%llu %u %llu)", - prev_key.objectid, prev_key.type, - prev_key.offset, key.objectid, key.type, - key.offset); + "bad key order, prev " BTRFS_KEY_FMT " current " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&prev_key), + BTRFS_KEY_FMT_VALUE(&key)); return BTRFS_TREE_BLOCK_BAD_KEY_ORDER; } @@ -2198,10 +2196,9 @@ enum btrfs_tree_block_status __btrfs_check_node(struct extent_buffer *node) if (unlikely(btrfs_comp_cpu_keys(&key, &next_key) >= 0)) { generic_err(node, slot, - "bad key order, current (%llu %u %llu) next (%llu %u %llu)", - key.objectid, key.type, key.offset, - next_key.objectid, next_key.type, - next_key.offset); + "bad key order, current " BTRFS_KEY_FMT " next " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&key), + BTRFS_KEY_FMT_VALUE(&next_key)); return BTRFS_TREE_BLOCK_BAD_KEY_ORDER; } } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index c45c5112c035..4fd9a417fc5f 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -199,9 +199,9 @@ static void do_abort_log_replay(struct walk_control *wc, const char *function, if (wc->log_leaf) { btrfs_crit(fs_info, - "log tree (for root %llu) leaf currently being processed (slot %d key %llu %u %llu):", +"log tree (for root %llu) leaf currently being processed (slot %d key " BTRFS_KEY_FMT "):", btrfs_root_id(wc->root), wc->log_slot, - wc->log_key.objectid, wc->log_key.type, wc->log_key.offset); + BTRFS_KEY_FMT_VALUE(&wc->log_key)); btrfs_print_leaf(wc->log_leaf); } @@ -511,9 +511,9 @@ static int overwrite_item(struct walk_control *wc) ret = btrfs_search_slot(NULL, root, &wc->log_key, wc->subvol_path, 0, 0); if (ret < 0) { btrfs_abort_log_replay(wc, ret, - "failed to search subvolume tree for key (%llu %u %llu) root %llu", - wc->log_key.objectid, wc->log_key.type, - wc->log_key.offset, btrfs_root_id(root)); + "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", + BTRFS_KEY_FMT_VALUE(&wc->log_key), + btrfs_root_id(root)); return ret; } @@ -619,9 +619,8 @@ static int overwrite_item(struct walk_control *wc) btrfs_extend_item(trans, wc->subvol_path, item_size - found_size); } else if (ret) { btrfs_abort_log_replay(wc, ret, - "failed to insert item for key (%llu %u %llu)", - wc->log_key.objectid, wc->log_key.type, - wc->log_key.offset); + "failed to insert item for key " BTRFS_KEY_FMT, + BTRFS_KEY_FMT_VALUE(&wc->log_key)); return ret; } dst_ptr = btrfs_item_ptr_offset(dst_eb, dst_slot); @@ -830,9 +829,9 @@ static noinline int replay_one_extent(struct walk_control *wc) &wc->log_key, sizeof(*item)); if (ret) { btrfs_abort_log_replay(wc, ret, - "failed to insert item with key (%llu %u %llu) root %llu", - wc->log_key.objectid, wc->log_key.type, - wc->log_key.offset, btrfs_root_id(root)); + "failed to insert item with key " BTRFS_KEY_FMT " root %llu", + BTRFS_KEY_FMT_VALUE(&wc->log_key), + btrfs_root_id(root)); goto out; } dest_offset = btrfs_item_ptr_offset(wc->subvol_path->nodes[0], @@ -1349,9 +1348,9 @@ static inline int __add_inode_ref(struct walk_control *wc, ret = btrfs_search_slot(NULL, root, &search_key, wc->subvol_path, 0, 0); if (ret < 0) { btrfs_abort_log_replay(wc, ret, - "failed to search subvolume tree for key (%llu %u %llu) root %llu", - search_key.objectid, search_key.type, - search_key.offset, btrfs_root_id(root)); + "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", + BTRFS_KEY_FMT_VALUE(&search_key), + btrfs_root_id(root)); return ret; } else if (ret == 0) { /* @@ -1484,9 +1483,9 @@ static int unlink_old_inode_refs(struct walk_control *wc, struct btrfs_inode *in } if (ret < 0) { btrfs_abort_log_replay(wc, ret, - "failed to search subvolume tree for key (%llu %u %llu) root %llu", - wc->log_key.objectid, wc->log_key.type, - wc->log_key.offset, btrfs_root_id(root)); + "failed to search subvolume tree for key " BTRFS_KEY_FMT " root %llu", + BTRFS_KEY_FMT_VALUE(&wc->log_key), + btrfs_root_id(root)); goto out; } @@ -2701,10 +2700,9 @@ static noinline int replay_dir_deletes(struct walk_control *wc, wc->subvol_path, 0, 0); if (ret < 0) { btrfs_abort_log_replay(wc, ret, - "failed to search root %llu for key (%llu %u %llu)", + "failed to search root %llu for key " BTRFS_KEY_FMT, btrfs_root_id(root), - dir_key.objectid, dir_key.type, - dir_key.offset); + BTRFS_KEY_FMT_VALUE(&dir_key)); goto out; } From 76ad957a72c713d48a4232bb56d6e19477d32094 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 18 Nov 2025 17:08:43 +0100 Subject: [PATCH 298/697] btrfs: don't search back for dir inode item in INO_LOOKUP_USER [ Upstream commit 70085399b1a1623ef488d96b4c2d0c67be1d0607 ] We don't need to search back to the inode item, the directory inode number is in key.offset, so simply use that. If we can't find the directory we'll get an ENOENT at the iget(). Note: The patch was taken from v5 of fscrypt patchset (https://lore.kernel.org/linux-btrfs/cover.1706116485.git.josef@toxicpanda.com/) which was handled over time by various people: Omar Sandoval, Sweet Tea Dorminy, Josef Bacik. Reviewed-by: Johannes Thumshirn Signed-off-by: Josef Bacik Signed-off-by: Daniel Vacek Reviewed-by: David Sterba [ add note ] Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/ioctl.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index c0691e93e0a5..41e549d37aac 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1832,7 +1832,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, struct btrfs_root_ref *rref; struct btrfs_root *root = NULL; struct btrfs_path *path; - struct btrfs_key key, key2; + struct btrfs_key key; struct extent_buffer *leaf; char *ptr; int slot; @@ -1887,24 +1887,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, read_extent_buffer(leaf, ptr, (unsigned long)(iref + 1), len); - /* Check the read+exec permission of this directory */ - ret = btrfs_previous_item(root, path, dirid, - BTRFS_INODE_ITEM_KEY); - if (ret < 0) { - goto out_put; - } else if (ret > 0) { - ret = -ENOENT; - goto out_put; - } - - leaf = path->nodes[0]; - slot = path->slots[0]; - btrfs_item_key_to_cpu(leaf, &key2, slot); - if (key2.objectid != dirid) { - ret = -ENOENT; - goto out_put; - } - /* * We don't need the path anymore, so release it and * avoid deadlocks and lockdep warnings in case @@ -1912,11 +1894,12 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, * btree and lock the same leaf. */ btrfs_release_path(path); - temp_inode = btrfs_iget(key2.objectid, root); + temp_inode = btrfs_iget(key.offset, root); if (IS_ERR(temp_inode)) { ret = PTR_ERR(temp_inode); goto out_put; } + /* Check the read+exec permission of this directory. */ ret = inode_permission(idmap, &temp_inode->vfs_inode, MAY_READ | MAY_EXEC); iput(&temp_inode->vfs_inode); From a1296bb9f44a446ba3f8e06a7cec575979e988a4 Mon Sep 17 00:00:00 2001 From: David Sterba Date: Tue, 18 Nov 2025 17:06:46 +0100 Subject: [PATCH 299/697] btrfs: remaining BTRFS_PATH_AUTO_FREE conversions [ Upstream commit 10934c131f9bcfb616dd8be9456f11efd6b240ec ] Do the remaining btrfs_path conversion to the auto cleaning, this seems to be the last one. Most of the conversions are trivial, only adding the declaration and removing the freeing, or changing the goto patterns to return. There are some functions with many changes, like __btrfs_free_extent(), btrfs_remove_from_free_space_tree() or btrfs_add_to_free_space_tree() but it still follows the same pattern. Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/block-group.c | 3 +- fs/btrfs/dir-item.c | 3 +- fs/btrfs/extent-tree.c | 41 +++++------ fs/btrfs/free-space-tree.c | 29 ++++---- fs/btrfs/inode-item.c | 3 +- fs/btrfs/inode.c | 3 +- fs/btrfs/ioctl.c | 37 ++++------ fs/btrfs/qgroup.c | 142 ++++++++++++++----------------------- fs/btrfs/super.c | 10 +-- fs/btrfs/tree-log.c | 4 +- fs/btrfs/volumes.c | 3 +- fs/btrfs/xattr.c | 3 +- 12 files changed, 105 insertions(+), 176 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index a277c8cc9166..1e57f7d04c47 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -1065,7 +1065,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, struct btrfs_chunk_map *map) { struct btrfs_fs_info *fs_info = trans->fs_info; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_block_group *block_group; struct btrfs_free_cluster *cluster; struct inode *inode; @@ -1305,7 +1305,6 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, btrfs_put_block_group(block_group); if (remove_rsv) btrfs_dec_delayed_refs_rsv_bg_updates(fs_info); - btrfs_free_path(path); return ret; } diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c index 77e1bcb2a74b..085a83ae9e62 100644 --- a/fs/btrfs/dir-item.c +++ b/fs/btrfs/dir-item.c @@ -112,7 +112,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, int ret = 0; int ret2 = 0; struct btrfs_root *root = dir->root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_dir_item *dir_item; struct extent_buffer *leaf; unsigned long name_ptr; @@ -164,7 +164,6 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, ret2 = btrfs_insert_delayed_dir_index(trans, name->name, name->len, dir, &disk_key, type, index); out_free: - btrfs_free_path(path); if (ret) return ret; if (ret2) diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index e40835fe4bde..663526d909ab 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3084,7 +3084,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, { struct btrfs_fs_info *info = trans->fs_info; struct btrfs_key key; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_root *extent_root; struct extent_buffer *leaf; struct btrfs_extent_item *ei; @@ -3119,7 +3119,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, node->bytenr, refs_to_drop); ret = -EINVAL; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } if (is_data) @@ -3164,15 +3164,14 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, abort_and_dump(trans, path, "invalid iref slot %u, no EXTENT/METADATA_ITEM found but has inline extent ref", path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } /* Must be SHARED_* item, remove the backref first */ ret = remove_extent_backref(trans, extent_root, path, NULL, refs_to_drop, is_data); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } btrfs_release_path(path); @@ -3221,7 +3220,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } if (unlikely(ret < 0)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } extent_slot = path->slots[0]; } @@ -3230,10 +3229,10 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, "unable to find ref byte nr %llu parent %llu root %llu owner %llu offset %llu slot %d", bytenr, node->parent, node->ref_root, owner_objectid, owner_offset, path->slots[0]); - goto out; + return ret; } else { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } leaf = path->nodes[0]; @@ -3244,7 +3243,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, "unexpected extent item size, has %u expect >= %zu", item_size, sizeof(*ei)); btrfs_abort_transaction(trans, ret); - goto out; + return ret; } ei = btrfs_item_ptr(leaf, extent_slot, struct btrfs_extent_item); @@ -3258,8 +3257,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, key.objectid, key.type, key.offset, path->slots[0], owner_objectid, item_size, sizeof(*ei) + sizeof(*bi)); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } bi = (struct btrfs_tree_block_info *)(ei + 1); WARN_ON(owner_objectid != btrfs_tree_block_level(leaf, bi)); @@ -3270,8 +3268,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, abort_and_dump(trans, path, "trying to drop %d refs but we only have %llu for bytenr %llu slot %u", refs_to_drop, refs, bytenr, path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } refs -= refs_to_drop; @@ -3287,8 +3284,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, abort_and_dump(trans, path, "invalid iref, got inlined extent ref but no EXTENT/METADATA_ITEM found, slot %u", path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } } else { btrfs_set_extent_refs(leaf, ei, refs); @@ -3298,7 +3294,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, iref, refs_to_drop, is_data); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } } } else { @@ -3318,8 +3314,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, "invalid refs_to_drop, current refs %u refs_to_drop %u slot %u", extent_data_ref_count(path, iref), refs_to_drop, path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } if (iref) { if (unlikely(path->slots[0] != extent_slot)) { @@ -3327,8 +3322,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, "invalid iref, extent item key " BTRFS_KEY_FMT " slot %u doesn't have wanted iref", BTRFS_KEY_FMT_VALUE(&key), path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } } else { /* @@ -3341,8 +3335,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, abort_and_dump(trans, path, "invalid SHARED_* item slot %u, previous item is not EXTENT/METADATA_ITEM", path->slots[0]); - ret = -EUCLEAN; - goto out; + return -EUCLEAN; } path->slots[0] = extent_slot; num_to_del = 2; @@ -3363,7 +3356,7 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, num_to_del); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } btrfs_release_path(path); @@ -3371,8 +3364,6 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, } btrfs_release_path(path); -out: - btrfs_free_path(path); return ret; } diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index d86541073d42..c3734892d654 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -841,7 +841,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, u64 start, u64 size) { struct btrfs_block_group *block_group; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); int ret; if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) @@ -851,7 +851,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, if (unlikely(!path)) { ret = -ENOMEM; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } block_group = btrfs_lookup_block_group(trans->fs_info, start); @@ -859,7 +859,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, DEBUG_WARN("no block group found for start=%llu", start); ret = -ENOENT; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } mutex_lock(&block_group->free_space_lock); @@ -869,8 +869,7 @@ int btrfs_remove_from_free_space_tree(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, ret); btrfs_put_block_group(block_group); -out: - btrfs_free_path(path); + return ret; } @@ -1023,7 +1022,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, u64 start, u64 size) { struct btrfs_block_group *block_group; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); int ret; if (!btrfs_fs_compat_ro(trans->fs_info, FREE_SPACE_TREE)) @@ -1033,7 +1032,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, if (unlikely(!path)) { ret = -ENOMEM; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } block_group = btrfs_lookup_block_group(trans->fs_info, start); @@ -1041,7 +1040,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, DEBUG_WARN("no block group found for start=%llu", start); ret = -ENOENT; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } mutex_lock(&block_group->free_space_lock); @@ -1051,8 +1050,7 @@ int btrfs_add_to_free_space_tree(struct btrfs_trans_handle *trans, btrfs_abort_transaction(trans, ret); btrfs_put_block_group(block_group); -out: - btrfs_free_path(path); + return ret; } @@ -1466,7 +1464,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, struct btrfs_block_group *block_group) { struct btrfs_root *root = btrfs_free_space_root(block_group); - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key, found_key; struct extent_buffer *leaf; u64 start, end; @@ -1485,7 +1483,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, if (unlikely(!path)) { ret = -ENOMEM; btrfs_abort_transaction(trans, ret); - goto out; + return ret; } start = block_group->start; @@ -1499,7 +1497,7 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, ret = btrfs_search_prev_slot(trans, root, &key, path, -1, 1); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } leaf = path->nodes[0]; @@ -1530,14 +1528,13 @@ int btrfs_remove_block_group_free_space(struct btrfs_trans_handle *trans, ret = btrfs_del_items(trans, root, path, path->slots[0], nr); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); - goto out; + return ret; } btrfs_release_path(path); } ret = 0; -out: - btrfs_free_path(path); + return ret; } diff --git a/fs/btrfs/inode-item.c b/fs/btrfs/inode-item.c index 1bd73b80f9fa..7e14e1bbcf38 100644 --- a/fs/btrfs/inode-item.c +++ b/fs/btrfs/inode-item.c @@ -444,7 +444,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, struct btrfs_truncate_control *control) { struct btrfs_fs_info *fs_info = root->fs_info; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct extent_buffer *leaf; struct btrfs_file_extent_item *fi; struct btrfs_key key; @@ -730,6 +730,5 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, if (!ret && control->last_size > new_size) control->last_size = new_size; - btrfs_free_path(path); return ret; } diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index dcc0e53782c6..a4f1810db079 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4448,7 +4448,7 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, { struct btrfs_root *root = dir->root; struct btrfs_inode *inode = BTRFS_I(d_inode(dentry)); - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct extent_buffer *leaf; struct btrfs_dir_item *di; struct btrfs_key key; @@ -4541,7 +4541,6 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, if (ret) btrfs_abort_transaction(trans, ret); out: - btrfs_free_path(path); fscrypt_free_filename(&fname); return ret; } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 41e549d37aac..2f1c5f5e2e72 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1613,7 +1613,7 @@ static noinline int search_ioctl(struct btrfs_root *root, { struct btrfs_fs_info *info = root->fs_info; struct btrfs_key key; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); int ret; int num_found = 0; unsigned long sk_offset = 0; @@ -1633,10 +1633,8 @@ static noinline int search_ioctl(struct btrfs_root *root, } else { /* Look up the root from the arguments. */ root = btrfs_get_fs_root(info, sk->tree_id, true); - if (IS_ERR(root)) { - btrfs_free_path(path); + if (IS_ERR(root)) return PTR_ERR(root); - } } key.objectid = sk->min_objectid; @@ -1670,7 +1668,6 @@ static noinline int search_ioctl(struct btrfs_root *root, sk->nr_items = num_found; btrfs_put_root(root); - btrfs_free_path(path); return ret; } @@ -1753,7 +1750,7 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, int total_len = 0; struct btrfs_inode_ref *iref; struct extent_buffer *l; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); if (dirid == BTRFS_FIRST_FREE_OBJECTID) { name[0]='\0'; @@ -1814,7 +1811,6 @@ static noinline int btrfs_search_path_in_tree(struct btrfs_fs_info *info, ret = 0; out: btrfs_put_root(root); - btrfs_free_path(path); return ret; } @@ -1831,7 +1827,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, struct btrfs_inode_ref *iref; struct btrfs_root_ref *rref; struct btrfs_root *root = NULL; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct extent_buffer *leaf; char *ptr; @@ -1852,10 +1848,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, ptr = &args->path[BTRFS_INO_LOOKUP_USER_PATH_MAX - 1]; root = btrfs_get_fs_root(fs_info, treeid, true); - if (IS_ERR(root)) { - ret = PTR_ERR(root); - goto out; - } + if (IS_ERR(root)) + return PTR_ERR(root); key.objectid = dirid; key.type = BTRFS_INODE_REF_KEY; @@ -1930,12 +1924,10 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, key.type = BTRFS_ROOT_REF_KEY; key.offset = args->treeid; ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0); - if (ret < 0) { - goto out; - } else if (ret > 0) { - ret = -ENOENT; - goto out; - } + if (ret < 0) + return ret; + else if (ret > 0) + return -ENOENT; leaf = path->nodes[0]; slot = path->slots[0]; @@ -1945,10 +1937,8 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, item_len = btrfs_item_size(leaf, slot); /* Check if dirid in ROOT_REF corresponds to passed dirid */ rref = btrfs_item_ptr(leaf, slot, struct btrfs_root_ref); - if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) { - ret = -EINVAL; - goto out; - } + if (args->dirid != btrfs_root_ref_dirid(leaf, rref)) + return -EINVAL; /* Copy subvolume's name */ item_off += sizeof(struct btrfs_root_ref); @@ -1958,8 +1948,7 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, out_put: btrfs_put_root(root); -out: - btrfs_free_path(path); + return ret; } diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 1f83af14568c..7b62d2faaf7e 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -660,7 +660,7 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, { int ret; struct btrfs_root *quota_root = trans->fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; path = btrfs_alloc_path(); @@ -672,7 +672,6 @@ static int add_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, key.offset = dst; ret = btrfs_insert_empty_item(trans, quota_root, path, &key, 0); - btrfs_free_path(path); return ret; } @@ -681,7 +680,7 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, { int ret; struct btrfs_root *quota_root = trans->fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; path = btrfs_alloc_path(); @@ -694,24 +693,19 @@ static int del_qgroup_relation_item(struct btrfs_trans_handle *trans, u64 src, ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); if (ret < 0) - goto out; + return ret; - if (ret > 0) { - ret = -ENOENT; - goto out; - } + if (ret > 0) + return -ENOENT; - ret = btrfs_del_item(trans, quota_root, path); -out: - btrfs_free_path(path); - return ret; + return btrfs_del_item(trans, quota_root, path); } static int add_qgroup_item(struct btrfs_trans_handle *trans, struct btrfs_root *quota_root, u64 qgroupid) { int ret; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_qgroup_info_item *qgroup_info; struct btrfs_qgroup_limit_item *qgroup_limit; struct extent_buffer *leaf; @@ -737,7 +731,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, ret = btrfs_insert_empty_item(trans, quota_root, path, &key, sizeof(*qgroup_info)); if (ret && ret != -EEXIST) - goto out; + return ret; leaf = path->nodes[0]; qgroup_info = btrfs_item_ptr(leaf, path->slots[0], @@ -754,7 +748,7 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, ret = btrfs_insert_empty_item(trans, quota_root, path, &key, sizeof(*qgroup_limit)); if (ret && ret != -EEXIST) - goto out; + return ret; leaf = path->nodes[0]; qgroup_limit = btrfs_item_ptr(leaf, path->slots[0], @@ -765,17 +759,14 @@ static int add_qgroup_item(struct btrfs_trans_handle *trans, btrfs_set_qgroup_limit_rsv_rfer(leaf, qgroup_limit, 0); btrfs_set_qgroup_limit_rsv_excl(leaf, qgroup_limit, 0); - ret = 0; -out: - btrfs_free_path(path); - return ret; + return 0; } static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid) { int ret; struct btrfs_root *quota_root = trans->fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; path = btrfs_alloc_path(); @@ -787,33 +778,27 @@ static int del_qgroup_item(struct btrfs_trans_handle *trans, u64 qgroupid) key.offset = qgroupid; ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); if (ret < 0) - goto out; + return ret; - if (ret > 0) { - ret = -ENOENT; - goto out; - } + if (ret > 0) + return -ENOENT; ret = btrfs_del_item(trans, quota_root, path); if (ret) - goto out; + return ret; btrfs_release_path(path); key.type = BTRFS_QGROUP_LIMIT_KEY; ret = btrfs_search_slot(trans, quota_root, &key, path, -1, 1); if (ret < 0) - goto out; + return ret; - if (ret > 0) { - ret = -ENOENT; - goto out; - } + if (ret > 0) + return -ENOENT; ret = btrfs_del_item(trans, quota_root, path); -out: - btrfs_free_path(path); return ret; } @@ -821,7 +806,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, struct btrfs_qgroup *qgroup) { struct btrfs_root *quota_root = trans->fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct extent_buffer *l; struct btrfs_qgroup_limit_item *qgroup_limit; @@ -841,7 +826,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, ret = -ENOENT; if (ret) - goto out; + return ret; l = path->nodes[0]; slot = path->slots[0]; @@ -851,8 +836,7 @@ static int update_qgroup_limit_item(struct btrfs_trans_handle *trans, btrfs_set_qgroup_limit_max_excl(l, qgroup_limit, qgroup->max_excl); btrfs_set_qgroup_limit_rsv_rfer(l, qgroup_limit, qgroup->rsv_rfer); btrfs_set_qgroup_limit_rsv_excl(l, qgroup_limit, qgroup->rsv_excl); -out: - btrfs_free_path(path); + return ret; } @@ -861,7 +845,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, { struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *quota_root = fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct extent_buffer *l; struct btrfs_qgroup_info_item *qgroup_info; @@ -884,7 +868,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, ret = -ENOENT; if (ret) - goto out; + return ret; l = path->nodes[0]; slot = path->slots[0]; @@ -894,8 +878,7 @@ static int update_qgroup_info_item(struct btrfs_trans_handle *trans, btrfs_set_qgroup_info_rfer_cmpr(l, qgroup_info, qgroup->rfer_cmpr); btrfs_set_qgroup_info_excl(l, qgroup_info, qgroup->excl); btrfs_set_qgroup_info_excl_cmpr(l, qgroup_info, qgroup->excl_cmpr); -out: - btrfs_free_path(path); + return ret; } @@ -903,7 +886,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) { struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_root *quota_root = fs_info->quota_root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct extent_buffer *l; struct btrfs_qgroup_status_item *ptr; @@ -923,7 +906,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) ret = -ENOENT; if (ret) - goto out; + return ret; l = path->nodes[0]; slot = path->slots[0]; @@ -933,8 +916,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) btrfs_set_qgroup_status_generation(l, ptr, trans->transid); btrfs_set_qgroup_status_rescan(l, ptr, fs_info->qgroup_rescan_progress.objectid); -out: - btrfs_free_path(path); + return ret; } @@ -944,7 +926,7 @@ static int update_qgroup_status_item(struct btrfs_trans_handle *trans) static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, struct btrfs_root *root) { - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct extent_buffer *leaf = NULL; int ret; @@ -961,7 +943,7 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, while (1) { ret = btrfs_search_slot(trans, root, &key, path, -1, 1); if (ret < 0) - goto out; + return ret; leaf = path->nodes[0]; nr = btrfs_header_nritems(leaf); if (!nr) @@ -974,14 +956,12 @@ static int btrfs_clean_quota_tree(struct btrfs_trans_handle *trans, path->slots[0] = 0; ret = btrfs_del_items(trans, root, path, 0, nr); if (ret) - goto out; + return ret; btrfs_release_path(path); } - ret = 0; -out: - btrfs_free_path(path); - return ret; + + return 0; } int btrfs_quota_enable(struct btrfs_fs_info *fs_info, @@ -1712,8 +1692,7 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) { struct btrfs_key key; - struct btrfs_path *path; - int ret; + BTRFS_PATH_AUTO_FREE(path); /* * Squota would never be inconsistent, but there can still be case @@ -1746,13 +1725,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup if (!path) return -ENOMEM; - ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); - btrfs_free_path(path); /* * The @ret from btrfs_find_root() exactly matches our definition for * the return value, thus can be returned directly. */ - return ret; + return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); } int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) @@ -2301,7 +2278,7 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, bool trace_leaf) { struct btrfs_key key; - struct btrfs_path *src_path; + BTRFS_PATH_AUTO_FREE(src_path); struct btrfs_fs_info *fs_info = trans->fs_info; u32 nodesize = fs_info->nodesize; int cur_level = root_level; @@ -2313,10 +2290,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, return -EINVAL; src_path = btrfs_alloc_path(); - if (!src_path) { - ret = -ENOMEM; - goto out; - } + if (!src_path) + return -ENOMEM; if (dst_level) btrfs_node_key_to_cpu(dst_path->nodes[dst_level], &key, 0); @@ -2342,10 +2317,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, parent_slot = src_path->slots[cur_level + 1]; eb = btrfs_read_node_slot(eb, parent_slot); - if (IS_ERR(eb)) { - ret = PTR_ERR(eb); - goto out; - } + if (IS_ERR(eb)) + return PTR_ERR(eb); src_path->nodes[cur_level] = eb; @@ -2366,10 +2339,8 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, &src_key, src_path->slots[cur_level]); } /* Content mismatch, something went wrong */ - if (btrfs_comp_cpu_keys(&dst_key, &src_key)) { - ret = -ENOENT; - goto out; - } + if (btrfs_comp_cpu_keys(&dst_key, &src_key)) + return -ENOENT; cur_level--; } @@ -2380,21 +2351,20 @@ static int qgroup_trace_extent_swap(struct btrfs_trans_handle* trans, ret = btrfs_qgroup_trace_extent(trans, src_path->nodes[dst_level]->start, nodesize); if (ret < 0) - goto out; + return ret; ret = btrfs_qgroup_trace_extent(trans, dst_path->nodes[dst_level]->start, nodesize); if (ret < 0) - goto out; + return ret; /* Record leaf file extents */ if (dst_level == 0 && trace_leaf) { ret = btrfs_qgroup_trace_leaf_items(trans, src_path->nodes[0]); if (ret < 0) - goto out; + return ret; ret = btrfs_qgroup_trace_leaf_items(trans, dst_path->nodes[0]); } -out: - btrfs_free_path(src_path); + return ret; } @@ -2595,7 +2565,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, int level; u8 drop_subptree_thres; struct extent_buffer *eb = root_eb; - struct btrfs_path *path = NULL; + BTRFS_PATH_AUTO_FREE(path); ASSERT(0 <= root_level && root_level < BTRFS_MAX_LEVEL); ASSERT(root_eb != NULL); @@ -2628,12 +2598,12 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, ret = btrfs_read_extent_buffer(root_eb, &check); if (ret) - goto out; + return ret; } if (root_level == 0) { ret = btrfs_qgroup_trace_leaf_items(trans, root_eb); - goto out; + return ret; } path = btrfs_alloc_path(); @@ -2669,10 +2639,8 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, child_bytenr = btrfs_node_blockptr(eb, parent_slot); eb = btrfs_read_node_slot(eb, parent_slot); - if (IS_ERR(eb)) { - ret = PTR_ERR(eb); - goto out; - } + if (IS_ERR(eb)) + return PTR_ERR(eb); path->nodes[level] = eb; path->slots[level] = 0; @@ -2683,14 +2651,14 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, ret = btrfs_qgroup_trace_extent(trans, child_bytenr, fs_info->nodesize); if (ret) - goto out; + return ret; } if (level == 0) { ret = btrfs_qgroup_trace_leaf_items(trans, path->nodes[level]); if (ret) - goto out; + return ret; /* Nonzero return here means we completed our search */ ret = adjust_slots_upwards(path, root_level); @@ -2704,11 +2672,7 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans, level--; } - ret = 0; -out: - btrfs_free_path(path); - - return ret; + return 0; } static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index c40944ca7b94..f1bfe97beacf 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -805,17 +805,15 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, struct btrfs_root_ref *root_ref; struct btrfs_inode_ref *inode_ref; struct btrfs_key key; - struct btrfs_path *path = NULL; + BTRFS_PATH_AUTO_FREE(path); char *name = NULL, *ptr; u64 dirid; int len; int ret; path = btrfs_alloc_path(); - if (!path) { - ret = -ENOMEM; - goto err; - } + if (!path) + return ERR_PTR(-ENOMEM); name = kmalloc(PATH_MAX, GFP_KERNEL); if (!name) { @@ -903,7 +901,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, fs_root = NULL; } - btrfs_free_path(path); if (ptr == name + PATH_MAX - 1) { name[0] = '/'; name[1] = '\0'; @@ -914,7 +911,6 @@ char *btrfs_get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info, err: btrfs_put_root(fs_root); - btrfs_free_path(path); kfree(name); return ERR_PTR(ret); } diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 4fd9a417fc5f..a0e12b4fb956 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2647,7 +2647,7 @@ static noinline int replay_dir_deletes(struct walk_control *wc, int ret = 0; struct btrfs_key dir_key; struct btrfs_key found_key; - struct btrfs_path *log_path; + BTRFS_PATH_AUTO_FREE(log_path); struct btrfs_inode *dir; dir_key.objectid = dirid; @@ -2664,7 +2664,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc, * we replay the deletes before we copy in the inode item from the log. */ if (IS_ERR(dir)) { - btrfs_free_path(log_path); ret = PTR_ERR(dir); if (ret == -ENOENT) ret = 0; @@ -2744,7 +2743,6 @@ static noinline int replay_dir_deletes(struct walk_control *wc, ret = 0; out: btrfs_release_path(wc->subvol_path); - btrfs_free_path(log_path); iput(&dir->vfs_inode); return ret; } diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index ef9f24076cca..630fb5885692 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4205,7 +4205,7 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) struct btrfs_root *chunk_root = fs_info->chunk_root; u64 chunk_type; struct btrfs_chunk *chunk; - struct btrfs_path *path = NULL; + BTRFS_PATH_AUTO_FREE(path); struct btrfs_key key; struct btrfs_key found_key; struct extent_buffer *leaf; @@ -4382,7 +4382,6 @@ static int __btrfs_balance(struct btrfs_fs_info *fs_info) goto again; } error: - btrfs_free_path(path); if (enospc_errors) { btrfs_info(fs_info, "%d enospc errors during balance", enospc_errors); diff --git a/fs/btrfs/xattr.c b/fs/btrfs/xattr.c index 79fb1614bd0c..b6f01d6c79e7 100644 --- a/fs/btrfs/xattr.c +++ b/fs/btrfs/xattr.c @@ -85,7 +85,7 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, { struct btrfs_dir_item *di = NULL; struct btrfs_root *root = BTRFS_I(inode)->root; - struct btrfs_path *path; + BTRFS_PATH_AUTO_FREE(path); size_t name_len = strlen(name); int ret = 0; @@ -212,7 +212,6 @@ int btrfs_setxattr(struct btrfs_trans_handle *trans, struct inode *inode, */ } out: - btrfs_free_path(path); if (!ret) { set_bit(BTRFS_INODE_COPY_EVERYTHING, &BTRFS_I(inode)->runtime_flags); From 22d558df51d90e62894b1b7320857ad47dc23d6b Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Mon, 1 Dec 2025 15:33:49 -0800 Subject: [PATCH 300/697] btrfs: check squota parent usage on membership change [ Upstream commit 9c46bcda5f347febdbb4d117fb21a37ffcec5fa4 ] We could have detected the quick inherit bug more directly if we had an extra warning about squota hierarchy consistency while modifying the hierarchy. In squotas, the parent usage always simply adds up to the sum of its children, so we can just check for that when changing membership and detect more accounting bugs. Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/qgroup.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 7b62d2faaf7e..db1df67bccf3 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -346,6 +346,42 @@ int btrfs_verify_qgroup_counts(const struct btrfs_fs_info *fs_info, u64 qgroupid } #endif +static bool squota_check_parent_usage(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *parent) +{ + u64 excl_sum = 0; + u64 rfer_sum = 0; + u64 excl_cmpr_sum = 0; + u64 rfer_cmpr_sum = 0; + struct btrfs_qgroup_list *glist; + int nr_members = 0; + bool mismatch; + + if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) + return false; + if (btrfs_qgroup_level(parent->qgroupid) == 0) + return false; + + /* Eligible parent qgroup. Squota; level > 0; empty members list. */ + list_for_each_entry(glist, &parent->members, next_member) { + excl_sum += glist->member->excl; + rfer_sum += glist->member->rfer; + excl_cmpr_sum += glist->member->excl_cmpr; + rfer_cmpr_sum += glist->member->rfer_cmpr; + nr_members++; + } + mismatch = (parent->excl != excl_sum || parent->rfer != rfer_sum || + parent->excl_cmpr != excl_cmpr_sum || parent->rfer_cmpr != excl_cmpr_sum); + + WARN(mismatch, + "parent squota qgroup %hu/%llu has mismatched usage from its %d members. " + "%llu %llu %llu %llu vs %llu %llu %llu %llu\n", + btrfs_qgroup_level(parent->qgroupid), + btrfs_qgroup_subvolid(parent->qgroupid), nr_members, parent->excl, + parent->rfer, parent->excl_cmpr, parent->rfer_cmpr, excl_sum, + rfer_sum, excl_cmpr_sum, rfer_cmpr_sum); + return mismatch; +} + __printf(2, 3) static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info, const char *fmt, ...) { @@ -1565,6 +1601,7 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst goto out; } ret = quick_update_accounting(fs_info, src, dst, 1); + squota_check_parent_usage(fs_info, parent); spin_unlock(&fs_info->qgroup_lock); out: kfree(prealloc); @@ -1623,6 +1660,8 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, spin_lock(&fs_info->qgroup_lock); del_relation_rb(fs_info, src, dst); ret = quick_update_accounting(fs_info, src, dst, -1); + ASSERT(parent); + squota_check_parent_usage(fs_info, parent); spin_unlock(&fs_info->qgroup_lock); } out: From b422609291f6158d0c12e8503124b1a3221d6d8d Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Mon, 1 Dec 2025 15:35:02 -0800 Subject: [PATCH 301/697] btrfs: relax squota parent qgroup deletion rule [ Upstream commit adb0af40fe89fd42f1ef277bf60d9cfa7c2ae472 ] Currently, with squotas, we do not allow removing a parent qgroup with no members if it still has usage accounted to it. This makes it really difficult to recover from accounting bugs, as we have no good way of getting back to 0 usage. Instead, allow deletion (it's safe at 0 members..) while still warning about the inconsistency by adding a squota parent check. Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Signed-off-by: David Sterba Stable-dep-of: 1e92637722ae ("btrfs: check for subvolume before deleting squota qgroup") Signed-off-by: Sasha Levin --- fs/btrfs/qgroup.c | 50 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index db1df67bccf3..c7bc2065bcd6 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1723,6 +1723,36 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) return ret; } +static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) + +{ + ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); + return list_empty(&qgroup->members); +} + +/* + * Return true if we can delete the squota qgroup and false otherwise. + * + * Rules for whether we can delete: + * + * A subvolume qgroup can be removed iff the subvolume is fully deleted, which + * is iff there is 0 usage in the qgroup. + * + * A higher level qgroup can be removed iff it has no members. + * Note: We audit its usage to warn on inconsitencies without blocking deletion. + */ +static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) +{ + ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); + + if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { + squota_check_parent_usage(fs_info, qgroup); + return can_delete_parent_qgroup(qgroup); + } + + return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); +} + /* * Return 0 if we can not delete the qgroup (not empty or has children etc). * Return >0 if we can delete the qgroup. @@ -1733,23 +1763,13 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup struct btrfs_key key; BTRFS_PATH_AUTO_FREE(path); - /* - * Squota would never be inconsistent, but there can still be case - * where a dropped subvolume still has qgroup numbers, and squota - * relies on such qgroup for future accounting. - * - * So for squota, do not allow dropping any non-zero qgroup. - */ - if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE && - (qgroup->rfer || qgroup->excl || qgroup->excl_cmpr || qgroup->rfer_cmpr)) - return 0; + /* Since squotas cannot be inconsistent, they have special rules for deletion. */ + if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) + return can_delete_squota_qgroup(fs_info, qgroup); /* For higher level qgroup, we can only delete it if it has no child. */ - if (btrfs_qgroup_level(qgroup->qgroupid)) { - if (!list_empty(&qgroup->members)) - return 0; - return 1; - } + if (btrfs_qgroup_level(qgroup->qgroupid)) + return can_delete_parent_qgroup(qgroup); /* * For level-0 qgroups, we can only delete it if it has no subvolume From ca56ffdb017b33e68c69d1b59428b7466f5a4a13 Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Mon, 11 May 2026 13:07:11 -0700 Subject: [PATCH 302/697] btrfs: check for subvolume before deleting squota qgroup [ Upstream commit 1e92637722ae4bd417f7a37e8d1485dc23b93935 ] The invariant that we want to maintain with subvolume qgroups is that the qgroup can only be deleted if there is no root. With squotas, we thought that it was sufficient to just check the usage, because we assumed that deleting a subvolume will drive it's qgroups usage to 0, and thus 0 usage implies no subvolume. However, this is false, for two reasons: - A subvol whose extents are all from before squotas was enabled. - A subvol that was created in this transaction and for which we have not yet run any delayed refs. In both cases, deleting the qgroup breaks the desired invariant and we are left with a subvolume with no qgroup but squotas are enabled. Fix this by unifying the deletion check logic between full qgroups and squotas. Squotas do all the same checks *and* the additional usage == 0 check, which is the one extra rule peculiar to squotas. Link: https://lore.kernel.org/linux-btrfs/adnBhWfJQ1n3hZC8@merlins.org/ Fixes: a8df35619948 ("btrfs: forbid deleting live subvol qgroup") Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/qgroup.c | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index c7bc2065bcd6..a0d364e009d8 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1723,32 +1723,24 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) return ret; } -static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup) - +static bool can_delete_parent_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) { ASSERT(btrfs_qgroup_level(qgroup->qgroupid)); + if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) + squota_check_parent_usage(fs_info, qgroup); return list_empty(&qgroup->members); } /* - * Return true if we can delete the squota qgroup and false otherwise. - * - * Rules for whether we can delete: - * - * A subvolume qgroup can be removed iff the subvolume is fully deleted, which - * is iff there is 0 usage in the qgroup. - * - * A higher level qgroup can be removed iff it has no members. - * Note: We audit its usage to warn on inconsitencies without blocking deletion. + * Because a shared extent can outlive its owning subvolume, we cannot delete a + * subvol squota qgroup until all of the extents it owns are gone, even if the + * subvolume itself has been deleted. */ -static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup) +static bool can_delete_squota_subvol_qgroup(struct btrfs_fs_info *fs_info, + struct btrfs_qgroup *qgroup) { ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE); - - if (btrfs_qgroup_level(qgroup->qgroupid) > 0) { - squota_check_parent_usage(fs_info, qgroup); - return can_delete_parent_qgroup(qgroup); - } + ASSERT(btrfs_qgroup_level(qgroup->qgroupid) == 0); return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr); } @@ -1762,14 +1754,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup { struct btrfs_key key; BTRFS_PATH_AUTO_FREE(path); - - /* Since squotas cannot be inconsistent, they have special rules for deletion. */ - if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) - return can_delete_squota_qgroup(fs_info, qgroup); + int ret; /* For higher level qgroup, we can only delete it if it has no child. */ if (btrfs_qgroup_level(qgroup->qgroupid)) - return can_delete_parent_qgroup(qgroup); + return can_delete_parent_qgroup(fs_info, qgroup); /* * For level-0 qgroups, we can only delete it if it has no subvolume @@ -1785,10 +1774,21 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup return -ENOMEM; /* - * The @ret from btrfs_find_root() exactly matches our definition for - * the return value, thus can be returned directly. + * Any subvol qgroup, regardless of mode, cannot be deleted if the + * subvol still exists. + */ + ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); + /* + * btrfs_find_root returns <0 on error, 0 if found, and >0 if not, + * so the "found" and "error" cases match our desired return values. */ - return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL); + if (ret <= 0) + return ret; + + /* Squotas require additional checks, even if the subvol is deleted. */ + if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE) + return can_delete_squota_subvol_qgroup(fs_info, qgroup); + return 1; } int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid) From 7e91d3a1a98a3748e079b24379873b0318d10178 Mon Sep 17 00:00:00 2001 From: Boris Burkov Date: Mon, 11 May 2026 19:53:46 -0700 Subject: [PATCH 303/697] btrfs: fix squota accounting during enable generation [ Upstream commit d7c600554816b8ef70adffe078a0e360c055d82b ] The first transaction that enables squotas is special and a bit tricky. We have to set BTRFS_FS_QUOTA_ENABLED after the transaction to avoid a deadlock, so any delayed refs that run before we set the bit are not squota accounted. For data this is fine, we don't get an owner_ref, so there is no real harm, it's as if the extent predated squotas. However for metadata, the tree block will have gen == enable_gen so when we free it later, we will decrement the squota accounting, which can result in an underflow. Before it is freed, btrfs check shows errors, as we have mismatched usage between the node generations/owners and the squota values. There are two angles to this fix: 1. For extents that come in delayed_refs that run during the enable_gen transaction, we must actually set enable_gen to the *next* transaction. That is the first transaction that we can really properly account in any way. 2. For extents that come in between the end of our transaction handle and the time we set the BTRFS_FS_QUOTA_ENABLED bit, we need an additional bit, BTRFS_FS_SQUOTA_ENABLING which only affects recording squota deltas, so we do pick up those extents. Otherwise, we would miss them, even for enable_gen + 1. Fixes: bd7c1ea3a302 ("btrfs: qgroup: check generation when recording simple quota delta") Reviewed-by: Qu Wenruo Signed-off-by: Boris Burkov Reviewed-by: David Sterba Signed-off-by: David Sterba Signed-off-by: Sasha Levin --- fs/btrfs/fs.h | 1 + fs/btrfs/qgroup.c | 31 +++++++++++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index acdb9e52f6fd..eccc61463947 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -136,6 +136,7 @@ enum { BTRFS_FS_LOG_RECOVERING, BTRFS_FS_OPEN, BTRFS_FS_QUOTA_ENABLED, + BTRFS_FS_SQUOTA_ENABLING, BTRFS_FS_UPDATE_UUID_TREE_GEN, BTRFS_FS_CREATING_FREE_SPACE_TREE, BTRFS_FS_BTREE_ERR, diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index a0d364e009d8..261aa6501920 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -1111,7 +1111,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, if (simple) { fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE; btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA); - btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid); + /* + * Set the enable generation to the next transaction, as we cannot + * ensure that extents written during this transaction will see any + * state we have set here. So we should treat all extents of the + * transaction as coming in before squotas was enabled. + */ + btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1); } else { fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT; } @@ -1214,7 +1220,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, goto out_free_path; } - fs_info->qgroup_enable_gen = trans->transid; + /* + * Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING + * under the transaction handle. We want to ensure that all extents in + * the next transaction definitely see them. + */ + if (simple) { + fs_info->qgroup_enable_gen = trans->transid + 1; + set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); + } mutex_unlock(&fs_info->qgroup_ioctl_lock); /* @@ -1228,9 +1242,15 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, */ ret = btrfs_commit_transaction(trans); trans = NULL; + mutex_lock(&fs_info->qgroup_ioctl_lock); - if (ret) + if (ret) { + if (simple) { + clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); + fs_info->qgroup_enable_gen = 0; + } goto out_free_path; + } /* * Set quota enabled flag after committing the transaction, to avoid @@ -1240,6 +1260,8 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info, spin_lock(&fs_info->qgroup_lock); fs_info->quota_root = quota_root; set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags); + if (simple) + clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags); spin_unlock(&fs_info->qgroup_lock); /* Skip rescan for simple qgroups. */ @@ -4930,7 +4952,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info, u64 num_bytes = delta->num_bytes; const int sign = (delta->is_inc ? 1 : -1); - if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE) + if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE && + !test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags)) return 0; if (!btrfs_is_fstree(root)) From fecfed41da734c006aff722ab67f1e6fe0c77eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Mon, 11 May 2026 13:42:02 -0300 Subject: [PATCH 304/697] ASoC: amd: acp-sdw-legacy: check CPU DAI name before logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 1afd8f06dcb1d561af3b239c5b14a88b87c13454 ] devm_kasprintf() can fail and return NULL. The legacy AMD SoundWire machine driver logs cpus->dai_name before checking the allocation result. Move the debug print after the NULL check, matching the ordering used by the SOF AMD SoundWire path after commit 5726b68473f7 ("ASoC: amd/sdw_utils: avoid NULL deref when devm_kasprintf() fails"). Fixes: 2981d9b0789c ("ASoC: amd: acp: add soundwire machine driver for legacy stack") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260511-asoc-amd-acp-sdw-legacy-dai-name-null-v1-1-dc6151b6da8a@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/amd/acp/acp-sdw-legacy-mach.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c index 798c73d7e26d..e87d9e9991e1 100644 --- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c +++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c @@ -244,9 +244,9 @@ static int create_sdw_dailink(struct snd_soc_card *card, cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", link_num, cpu_pin_id); - dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); if (!cpus->dai_name) return -ENOMEM; + dev_dbg(dev, "cpu->dai_name:%s\n", cpus->dai_name); codec_maps[j].cpu = 0; codec_maps[j].codec = j; From 0fa225896f4b4cb24c519e1173e9e3347189bc23 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Sun, 10 May 2026 01:55:37 +0800 Subject: [PATCH 305/697] spi: mtk-snfi: Fix resource leak in mtk_snand_read_page_cache() [ Upstream commit 496ba79b9496b8b3747cbc764ebd33ee7325e806 ] When DMA read times out in mtk_snand_read_page_cache(), the original code erroneously jumped to cleanup label which skips DMA unmapping and ECC disable, causing a resource leak. Fixes: 764f1b748164 ("spi: add driver for MTK SPI NAND Flash Interface") Signed-off-by: Felix Gu Link: https://patch.msgid.link/20260510-snfi-v1-1-bc375cf1af8e@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- drivers/spi/spi-mtk-snfi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-mtk-snfi.c b/drivers/spi/spi-mtk-snfi.c index a026b0e61994..9e6038a5a2ad 100644 --- a/drivers/spi/spi-mtk-snfi.c +++ b/drivers/spi/spi-mtk-snfi.c @@ -961,7 +961,7 @@ static int mtk_snand_read_page_cache(struct mtk_snand *snf, &snf->op_done, usecs_to_jiffies(SNFI_POLL_INTERVAL))) { dev_err(snf->dev, "DMA timed out for reading from cache.\n"); ret = -ETIMEDOUT; - goto cleanup; + goto cleanup2; } // Wait for BUS_SEC_CNTR returning expected value From df19b6af171695a1352314597c9a4311d48d5171 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 12 May 2026 11:30:49 +0200 Subject: [PATCH 306/697] netfilter: nft_inner: release local_lock before re-enabling softirqs [ Upstream commit a6cb3ff979855f7f0ee9450a947fe8f96c2ba37a ] Quoting sashiko: In the error path, local_bh_enable() is called before local_unlock_nested_bh(). Fixes: ba36fada9ab4 ("netfilter: nft_inner: Use nested-BH locking for nft_pcpu_tun_ctx") Signed-off-by: Florian Westphal Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Pablo Neira Ayuso Signed-off-by: Sasha Levin --- net/netfilter/nft_inner.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index 1b3e7a976f56..ad08a43535b5 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -246,8 +246,8 @@ static bool nft_inner_restore_tun_ctx(const struct nft_pktinfo *pkt, local_lock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); this_cpu_tun_ctx = this_cpu_ptr(&nft_pcpu_tun_ctx.ctx); if (this_cpu_tun_ctx->cookie != (unsigned long)pkt->skb) { - local_bh_enable(); local_unlock_nested_bh(&nft_pcpu_tun_ctx.bh_lock); + local_bh_enable(); return false; } *tun_ctx = *this_cpu_tun_ctx; From 0c9e4d9484cc33a3e1425bb505145d80d1b09401 Mon Sep 17 00:00:00 2001 From: Eric Naim Date: Sat, 16 May 2026 19:15:31 +0800 Subject: [PATCH 307/697] ALSA: hda/realtek: Use ALC287_FIXUP_TXNW2781_I2C for ASUS Strix Gxx5 [ Upstream commit 4372286ac774536e8e68bc6dfa0f0b0152b31fce ] These devices were incorrectly using the ALC287_FIXUP_TAS2781_I2C quirk leading to errors: [ 18.765990] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found [ 18.768153] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found [ 18.768476] Serial bus multi instantiate pseudo device driver TXNW2781:00: error -ENXIO: IRQ index 0 not found [ 18.768899] Serial bus multi instantiate pseudo device driver TXNW2781:00: Instantiated 3 I2C devices. Use the ALC287_FIXUP_TXNW2781_I2C quirk instead to fix this and restore speaker audio on affected devices. Fixes: 1e9c708dc3ae ("ALSA: hda/tas2781: Add new quirk for Lenovo, ASUS, Dell projects") Link: https://lore.kernel.org/59fd4aa4-76b9-4984-8db9-a60e55ec6e80@losource.net/ Closes: https://lore.kernel.org/CACB9z7kjs8rhLstEc8fV29BCTb5dd881JwGozoKdO5cwCb=YwQ@mail.gmail.com Signed-off-by: Eric Naim Link: https://patch.msgid.link/20260516111532.111463-1-dnaim@cachyos.org Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/hda/codecs/realtek/alc269.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index ea98cbc4310d..4cd5b719556e 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7182,12 +7182,12 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x3e00, "ASUS G814FH/FM/FP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x3e20, "ASUS G814PH/PM/PP", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x1043, 0x3e30, "ASUS TP3607SA", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TAS2781_I2C), - SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TAS2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ee0, "ASUS Strix G815_JHR_JMR_JPR", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C), + SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C), SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2), SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC), From cdd1aaf0ee962f50810b9aef7928f2313989d55f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 16 May 2026 14:53:45 +0300 Subject: [PATCH 308/697] drm/msm/snapshot: fix dumping of the unaligned regions [ Upstream commit 76824d2467feb1828b745d6add2541918d7be3da ] The snapshotting code internally aligns data segment to 16 bytes. This works fine for DPU code (where most of the regions are aligned), but fails for snapshotting of the DSI data (because DSI data region is shifted by 4 bytes). Fix the code by removing length alignment and by accurately printing last registers in the region. While reworking the code also fix the 16x memory overallocation in msm_disp_state_dump_regs(). Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot") Reported-by: Salendarsingh Gaud Signed-off-by: Dmitry Baryshkov Patchwork: https://patchwork.freedesktop.org/patch/725449/ Message-ID: <20260516-msm-fix-dsi-dump-2-v2-1-9e49fb2d240e@oss.qualcomm.com> Signed-off-by: Rob Clark Signed-off-by: Sasha Levin --- .../gpu/drm/msm/disp/msm_disp_snapshot_util.c | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c index 071bcdea80f7..591507db2646 100644 --- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c +++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c @@ -9,7 +9,7 @@ #include "msm_disp_snapshot.h" -static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *base_addr) +static void msm_disp_state_dump_regs(u32 **reg, u32 len, void __iomem *base_addr) { u32 len_padded; u32 num_rows; @@ -19,11 +19,11 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b void __iomem *end_addr; int i; - len_padded = aligned_len * REG_DUMP_ALIGN; - num_rows = aligned_len / REG_DUMP_ALIGN; + len_padded = round_up(len, REG_DUMP_ALIGN); + num_rows = DIV_ROUND_UP(len, REG_DUMP_ALIGN); addr = base_addr; - end_addr = base_addr + aligned_len; + end_addr = base_addr + len; *reg = kvzalloc(len_padded, GFP_KERNEL); if (!*reg) @@ -48,8 +48,8 @@ static void msm_disp_state_dump_regs(u32 **reg, u32 aligned_len, void __iomem *b static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, void __iomem *base_addr, struct drm_printer *p) { + void __iomem *addr, *end_addr; int i; - void __iomem *addr; u32 num_rows; if (!dump_addr) { @@ -58,6 +58,7 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, } addr = base_addr; + end_addr = base_addr + len; num_rows = len / REG_DUMP_ALIGN; for (i = 0; i < num_rows; i++) { @@ -67,6 +68,17 @@ static void msm_disp_state_print_regs(const u32 *dump_addr, u32 len, dump_addr[i * 4 + 2], dump_addr[i * 4 + 3]); addr += REG_DUMP_ALIGN; } + + if (addr != end_addr) { + drm_printf(p, "0x%lx : %08x", + (unsigned long)(addr - base_addr), + dump_addr[i * 4]); + if (addr + 0x4 < end_addr) + drm_printf(p, " %08x", dump_addr[i * 4 + 1]); + if (addr + 0x8 < end_addr) + drm_printf(p, " %08x", dump_addr[i * 4 + 2]); + drm_printf(p, "\n"); + } } void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p) @@ -186,7 +198,7 @@ void msm_disp_snapshot_add_block(struct msm_disp_state *disp_state, u32 len, va_end(va); INIT_LIST_HEAD(&new_blk->node); - new_blk->size = ALIGN(len, REG_DUMP_ALIGN); + new_blk->size = len; new_blk->base_addr = base_addr; msm_disp_state_dump_regs(&new_blk->state, new_blk->size, base_addr); From c98107817b0f6cdf51adc5e84e75c39ee25d8b28 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 14 May 2026 14:31:49 -0700 Subject: [PATCH 309/697] hwmon: (lm90) Stop work before releasing hwmon device [ Upstream commit b09a45601094c7f4ec4db8090b825fa61e169d93 ] Sashiko reports: In lm90_probe(), the devm action to cancel the alert_work and report_work (lm90_restore_conf) is registered in lm90_init_client() before devm_hwmon_device_register_with_info() is called. Because devm executes cleanup actions in reverse order during module unbind or probe failure, the hwmon device is unregistered and freed first. If lm90_alert_work() or lm90_report_alarms() runs in the window between the hwmon device being freed and the delayed works being cancelled, lm90_update_alarms() will dereference the freed data->hwmon_dev here. Fix the problem by canceling the workers separately after registering the hwmon device and before registering the interrupt handler. This ensures that the workers are canceled after interrupts are disabled and before the hwmon device is released. Add "shutdown" flag to indicate that device shutdown is in progress to prevent workers from being re-armed. Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") Reported-by: Sashiko Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/lm90.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c1f528e292f3..41ee2309fe8a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -738,6 +738,7 @@ struct lm90_data { struct mutex update_lock; struct delayed_work alert_work; struct work_struct report_work; + bool shutdown; /* true if shutting down */ bool valid; /* true if register values are valid */ bool alarms_valid; /* true if status register values are valid */ unsigned long last_updated; /* in jiffies */ @@ -1156,6 +1157,9 @@ static void lm90_report_alarms(struct work_struct *work) static int lm90_update_alarms_locked(struct lm90_data *data, bool force) { + if (data->shutdown) + return 0; + if (force || !data->alarms_valid || time_after(jiffies, data->alarms_updated + msecs_to_jiffies(data->update_interval))) { struct i2c_client *client = data->client; @@ -2600,15 +2604,23 @@ static void lm90_restore_conf(void *_data) struct lm90_data *data = _data; struct i2c_client *client = data->client; - cancel_delayed_work_sync(&data->alert_work); - cancel_work_sync(&data->report_work); - /* Restore initial configuration */ if (data->flags & LM90_HAVE_CONVRATE) lm90_write_convrate(data, data->convrate_orig); lm90_write_reg(client, LM90_REG_CONFIG1, data->config_orig); } +static void lm90_stop_work(void *_data) +{ + struct lm90_data *data = _data; + + hwmon_lock(data->hwmon_dev); + data->shutdown = true; + hwmon_unlock(data->hwmon_dev); + cancel_delayed_work_sync(&data->alert_work); + cancel_work_sync(&data->report_work); +} + static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) { struct device_node *np = client->dev.of_node; @@ -2919,6 +2931,10 @@ static int lm90_probe(struct i2c_client *client) data->hwmon_dev = hwmon_dev; + err = devm_add_action_or_reset(&client->dev, lm90_stop_work, data); + if (err) + return err; + if (client->irq) { dev_dbg(dev, "IRQ: %d\n", client->irq); err = devm_request_threaded_irq(dev, client->irq, @@ -2947,7 +2963,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); - if ((data->flags & LM90_HAVE_BROKEN_ALERT) && + if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && (data->current_alarms & data->alert_alarms)) { if (!(data->config & 0x80)) { dev_dbg(&client->dev, "Disabling ALERT#\n"); From bed1fc32e0eb653806fa98afcf55f9a311fc4ce2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 14 May 2026 14:41:00 -0700 Subject: [PATCH 310/697] hwmon: (lm90) Add lock protection to lm90_alert [ Upstream commit 873e919e3101063a7a75989510ccfc125a4391cf ] Sashiko reports: lm90_alert() executes in the smbus alert context and calls lm90_update_confreg() to disable the hardware alert line, without acquiring hwmon_lock. Concurrently, sysfs write operations (such as lm90_write_convrate) hold the hwmon_lock, temporarily modify data->config, and then restore it. If an alert interrupt occurs concurrently with a sysfs write, the sysfs path will overwrite the alert handler's modifications to data->config and the hardware register. This unintentionally re-enables the hardware alert line while the alarm is still active, causing an interrupt storm. Add the missing lock to lm90_alert() to solve the problem. Fixes: 7a1d220ccb0cc ("hwmon: (lm90) Introduce function to update configuration register") Reported-by: Sashiko Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin --- drivers/hwmon/lm90.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 41ee2309fe8a..a465a8a7ef5a 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2963,6 +2963,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, */ struct lm90_data *data = i2c_get_clientdata(client); + hwmon_lock(data->hwmon_dev); if (!data->shutdown && (data->flags & LM90_HAVE_BROKEN_ALERT) && (data->current_alarms & data->alert_alarms)) { if (!(data->config & 0x80)) { @@ -2972,6 +2973,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, schedule_delayed_work(&data->alert_work, max_t(int, HZ, msecs_to_jiffies(data->update_interval))); } + hwmon_unlock(data->hwmon_dev); } else { dev_dbg(&client->dev, "Everything OK\n"); } From 9e360e610a73f62432e986775023d5382773f045 Mon Sep 17 00:00:00 2001 From: Cole Leavitt Date: Sat, 4 Apr 2026 22:41:44 -0700 Subject: [PATCH 311/697] wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is disabled [ Upstream commit 92cee08dc4f00e77fd1317e4343c5d458b0abab7 ] When the TLC notification disables AMSDU for a TID, the MLD driver sets max_tid_amsdu_len to the sentinel value 1. The TSO segmentation path in iwl_mld_tx_tso_segment() checks for zero but not for this sentinel, allowing it to reach the num_subframes calculation: num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad) = (1 + 2) / (1534 + 2) = 0 This zero propagates to iwl_tx_tso_segment() which sets: gso_size = num_subframes * mss = 0 Calling skb_gso_segment() with gso_size=0 creates over 32000 tiny segments from a single GSO skb. This floods the TX ring with ~1024 micro-frames (the rest are purged), creating a massive burst of TX completion events that can lead to memory corruption and a subsequent use-after-free in TCP's retransmit queue (refcount underflow in tcp_shifted_skb, NULL deref in tcp_rack_detect_loss). The MVM driver is immune because it checks mvmsta->amsdu_enabled before reaching the num_subframes calculation. The MLD driver has no equivalent bitmap check and relies solely on max_tid_amsdu_len, which does not catch the sentinel value. Fix this by detecting the sentinel value (max_tid_amsdu_len == 1) at the existing check and falling back to non-AMSDU TSO segmentation. Also add a WARN_ON_ONCE guard after the num_subframes division as defense-in-depth to catch any future code paths that produce zero through a different mechanism. Suggested-by: Miriam Rachel Korenblit Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver") Signed-off-by: Cole Leavitt Link: https://patch.msgid.link/20260405054145.1064152-3-cole@unwrap.rs Signed-off-by: Miri Korenblit Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mld/tx.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c index c54ea3a91b66..a60bfb1a2ab2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -828,7 +828,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, return -EINVAL; max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid]; - if (!max_tid_amsdu_len) + if (!max_tid_amsdu_len || max_tid_amsdu_len == 1) return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); /* Sub frame header + SNAP + IP header + TCP header + MSS */ @@ -840,6 +840,9 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, */ num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad); + if (WARN_ON_ONCE(!num_subframes)) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + if (sta->max_amsdu_subframes && num_subframes > sta->max_amsdu_subframes) num_subframes = sta->max_amsdu_subframes; From 3a74aaad047353da3344aed32e9042d4f334f926 Mon Sep 17 00:00:00 2001 From: Miri Korenblit Date: Fri, 15 May 2026 15:14:56 +0300 Subject: [PATCH 312/697] wifi: iwlwifi: mld: don't dereference a pointer before NULL checking it [ Upstream commit d733ed481fd20a8e7bfe5119c4e77761ba3f87ee ] In iwl_mld_remove_link, the link->fw_id is saved at the beginning of the function so we have it after we freed the link. But the link pointer can be NULL, and is not checked when the fw_id is stored. Fix it by simply freeing the link at the end of the function. fFixes: 0e66a39f4f0e ("wifi: iwlwifi: fix potential use after free in iwl_mld_remove_link()") Reviewed-by: Johannes Berg Link: https://patch.msgid.link/20260515151351.371f40fc6711.I6a82cfe9655564e9c5731af91c36493b26b1208e@changeid Signed-off-by: Miri Korenblit Signed-off-by: Sasha Levin --- drivers/net/wireless/intel/iwlwifi/mld/link.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c index f6f52d297a72..e67ba3a24d02 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/link.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include "constants.h" @@ -501,7 +501,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld, struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); bool is_deflink = link == &mld_vif->deflink; - u8 fw_id = link->fw_id; if (WARN_ON(!link || link->active)) return; @@ -509,15 +508,15 @@ void iwl_mld_remove_link(struct iwl_mld *mld, iwl_mld_rm_link_from_fw(mld, bss_conf); /* Continue cleanup on failure */ - if (!is_deflink) - kfree_rcu(link, rcu_head); - RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); - if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links)) + if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) return; - RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL); + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); + + if (!is_deflink) + kfree_rcu(link, rcu_head); } void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, From 181e67bc11c5ec5b87c6c512c2078752b23ca8d4 Mon Sep 17 00:00:00 2001 From: Jianpeng Chang Date: Wed, 13 May 2026 15:22:09 +0800 Subject: [PATCH 313/697] dma-mapping: move dma_map_resource() sanity check into debug code [ Upstream commit af0c3f05866237f7592219bfe05387bc3bfc99b5 ] dma_map_resource() uses pfn_valid() to ensure the range is not RAM. However, pfn_valid() only checks for availability of the memory map for a PFN but it does not ensure that the PFN is actually backed by RAM. On ARM64 with SPARSEMEM (128MB section granularity), MMIO addresses that share a section with RAM will falsely trigger the WARN_ON_ONCE and cause dma_map_resource() to return DMA_MAPPING_ERROR. This causes a WARNING on Raspberry Pi 4 during spi_bcm2835 probe because the SPI FIFO register (0xfe204004) falls in the same sparsemem section as the end of RAM (0xf8000000-0xfbffffff), both in section 31 (0xf8000000-0xffffffff). Move the sanity check from dma_map_resource() into debug_dma_map_phys() and replace the unreliable pfn_valid() with pfn_valid() && !PageReserved(), which correctly identifies actual usable RAM without false positives for MMIO regions that happen to have struct pages. Since dma_map_resource() is dma_map_phys(DMA_ATTR_MMIO), the check applies equally to both APIs. Any non-reserved page represents kernel memory to a sufficient degree that using DMA_ATTR_MMIO on it is almost certainly wrong and risks breaking coherency on non-coherent platforms. ZONE_DEVICE pages used for PCI P2P DMA (MEMORY_DEVICE_PCI_P2PDMA) have PageReserved set, so they will not trigger a false positive. The check no longer blocks the mapping and uses err_printk() to integrate with dma-debug filtering. Fixes: f7326196a781 ("dma-mapping: export new dma_*map_phys() interface") Reviewed-by: Robin Murphy Signed-off-by: Jianpeng Chang Reviewed-by: Leon Romanovsky Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/20260513072209.1486986-1-jianpeng.chang.cn@windriver.com Signed-off-by: Sasha Levin --- kernel/dma/debug.c | 9 ++++++++- kernel/dma/mapping.c | 4 ---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c index 21db33118591..fa4aac333917 100644 --- a/kernel/dma/debug.c +++ b/kernel/dma/debug.c @@ -1250,7 +1250,14 @@ void debug_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size, entry->direction = direction; entry->map_err_type = MAP_ERR_NOT_CHECKED; - if (!(attrs & DMA_ATTR_MMIO)) { + if (attrs & DMA_ATTR_MMIO) { + unsigned long pfn = PHYS_PFN(phys); + + if (pfn_valid(pfn) && !PageReserved(pfn_to_page(pfn))) + err_printk(dev, entry, + "dma_map_resource called for RAM address %pa\n", + &phys); + } else { check_for_stack(dev, phys); if (!PhysHighMem(phys)) diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index fe7472f13b10..35e4556b9556 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -366,10 +366,6 @@ EXPORT_SYMBOL(dma_unmap_sg_attrs); dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { - if (IS_ENABLED(CONFIG_DMA_API_DEBUG) && - WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr)))) - return DMA_MAPPING_ERROR; - return dma_map_phys(dev, phys_addr, size, dir, attrs | DMA_ATTR_MMIO); } EXPORT_SYMBOL(dma_map_resource); From 2c890e71ae26fa32f5a96c3694b71a2c310940e7 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Mon, 11 May 2026 15:41:34 +0000 Subject: [PATCH 314/697] drm/xe/gsc: Fix double-free of managed BO in error path [ Upstream commit d3ded53fab90996e7d94a39049e11962dd066725 ] The error path in xe_gsc_init_post_hwconfig() explicitly frees a BO allocated with xe_managed_bo_create_pin_map() via xe_bo_unpin_map_no_vm(). Since the managed BO already has a devm cleanup action registered, this causes a double-free when devm unwinds during probe failure. Remove the explicit free and let devm handle it, consistent with all other xe_managed_bo_create_pin_map() callers. Fixes: 2e5d47fe7839 ("drm/xe/uc: Use managed bo for HuC and GSC objects") Reviewed-by: Daniele Ceraolo Spurio Assisted-by: Claude:claude-opus-4.6 Link: https://patch.msgid.link/20260511154134.223696-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 71d61e3e299a17139e47f980a4d6f425b2c59bf7) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_gsc.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c index 8371ec002e4e..2a496987b829 100644 --- a/drivers/gpu/drm/xe/xe_gsc.c +++ b/drivers/gpu/drm/xe/xe_gsc.c @@ -487,8 +487,7 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) EXEC_QUEUE_FLAG_PERMANENT, 0); if (IS_ERR(q)) { xe_gt_err(gt, "Failed to create queue for GSC submission\n"); - err = PTR_ERR(q); - goto out_bo; + return PTR_ERR(q); } wq = alloc_ordered_workqueue("gsc-ordered-wq", 0); @@ -511,8 +510,6 @@ int xe_gsc_init_post_hwconfig(struct xe_gsc *gsc) out_q: xe_exec_queue_put(q); -out_bo: - xe_bo_unpin_map_no_vm(bo); return err; } From dc26e00860a15e5fbe000a74276dccd1690a0420 Mon Sep 17 00:00:00 2001 From: Michal Wajdeczko Date: Thu, 14 May 2026 17:57:26 +0200 Subject: [PATCH 315/697] drm/xe/vf: Fix signature of print functions [ Upstream commit 9bb2f1d7e6e58b8e434ddc2048c661bf87ccdf2a ] We have plugged-in existing VF print functions into our GT debugfs show helper as-is, but we missed that the helper expects functions to return int, while they were defined as void. This can lead to errors being reported when CFI is enabled. Fixes: 63d8cb8fe3dd ("drm/xe/vf: Expose SR-IOV VF attributes to GT debugfs") Signed-off-by: Michal Wajdeczko Cc: Mohanram Meenakshisundaram Reviewed-by: Shuicheng Lin Link: https://patch.msgid.link/20260514155726.7165-1-michal.wajdeczko@intel.com (cherry picked from commit 314e31c9a8a1c421ee4f7f755b9348aefbbca090) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_gt_sriov_vf.c | 24 ++++++++++++++++++------ drivers/gpu/drm/xe/xe_gt_sriov_vf.h | 6 +++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c index 0461d5513487..ca58ef3f9fd3 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c @@ -1030,13 +1030,15 @@ void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val) } /** - * xe_gt_sriov_vf_print_config - Print VF self config. + * xe_gt_sriov_vf_print_config() - Print VF self config. * @gt: the &xe_gt * @p: the &drm_printer * * This function is for VF use only. + * + * Return: always 0. */ -void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) +int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) { struct xe_gt_sriov_vf_selfconfig *config = >->sriov.vf.self_config; struct xe_device *xe = gt_to_xe(gt); @@ -1060,16 +1062,20 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p) drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs); drm_printf(p, "GuC doorbells:\t%u\n", config->num_dbs); + + return 0; } /** - * xe_gt_sriov_vf_print_runtime - Print VF's runtime regs received from PF. + * xe_gt_sriov_vf_print_runtime() - Print VF's runtime regs received from PF. * @gt: the &xe_gt * @p: the &drm_printer * * This function is for VF use only. + * + * Return: always 0. */ -void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) +int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) { struct vf_runtime_reg *vf_regs = gt->sriov.vf.runtime.regs; unsigned int size = gt->sriov.vf.runtime.num_regs; @@ -1078,16 +1084,20 @@ void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p) for (; size--; vf_regs++) drm_printf(p, "%#x = %#x\n", vf_regs->offset, vf_regs->value); + + return 0; } /** - * xe_gt_sriov_vf_print_version - Print VF ABI versions. + * xe_gt_sriov_vf_print_version() - Print VF ABI versions. * @gt: the &xe_gt * @p: the &drm_printer * * This function is for VF use only. + * + * Return: always 0. */ -void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) +int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) { struct xe_device *xe = gt_to_xe(gt); struct xe_uc_fw_version *guc_version = >->sriov.vf.guc_version; @@ -1117,4 +1127,6 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p) GUC_RELAY_VERSION_LATEST_MAJOR, GUC_RELAY_VERSION_LATEST_MINOR); drm_printf(p, "\thandshake:\t%u.%u\n", pf_version->major, pf_version->minor); + + return 0; } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h index 0af1dc769fe0..7f6c59b1ef7b 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h @@ -35,8 +35,8 @@ s64 xe_gt_sriov_vf_ggtt_shift(struct xe_gt *gt); u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg); void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val); -void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); -void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); -void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); +int xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p); +int xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p); +int xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p); #endif From 6c9e9272bc37f1bebab59f244c3e4c296a573dbc Mon Sep 17 00:00:00 2001 From: Mohanram Meenakshisundaram Date: Thu, 14 May 2026 23:19:18 +0530 Subject: [PATCH 316/697] drm/xe/pf: Fix CFI failure in debugfs access [ Upstream commit 96bf49b526e2d03a2b7f6e861925a08f46ed0d28 ] Reading debugfs file (/sys/kernel/debug/dri/0/gt*/pf/adverse_events) with CFI (Control Flow Integrity) enabled, the kernel panics at xe_gt_debugfs_simple_show+0x82/0xc0. xe_gt_debugfs_simple_show() declare a function pointer expecting int return type, but xe_gt_sriov_pf_monitor_print_events() is void return type, leading to CFI failure and kernel panic. [507620.973657] CFI failure at xe_gt_debugfs_simple_show+0x82/0xc0 [xe] (target: xe_gt_sriov_pf_monitor_print_events+0x0/0x130 [xe]; expected type: 0xd72c7139) Fix xe_gt_sriov_pf_monitor_print_events() function by updating to return an int type. Fixes: 1c99d3d3edab ("drm/xe/pf: Expose PF monitor details via debugfs") Signed-off-by: Mohanram Meenakshisundaram Reviewed-by: Michal Wajdeczko Signed-off-by: Michal Wajdeczko Link: https://patch.msgid.link/20260514174918.1556357-2-mohanram.meenakshisundaram@intel.com (cherry picked from commit ff1d386a8359746d9699ac30336e3b0684c68958) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c | 6 +++++- drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c index 7d532bded02a..a85ba4435378 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.c @@ -114,8 +114,10 @@ int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 * VFs with no events are not printed. * * This function can only be called on PF. + * + * Return: always 0 */ -void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) +int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p) { unsigned int n, total_vfs = xe_gt_sriov_pf_get_totalvfs(gt); const struct xe_gt_sriov_monitor *data; @@ -144,4 +146,6 @@ void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p #undef __format #undef __value } + + return 0; } diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h index 7ca9351a271b..0b8f088d3a16 100644 --- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h +++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_monitor.h @@ -13,7 +13,7 @@ struct drm_printer; struct xe_gt; void xe_gt_sriov_pf_monitor_flr(struct xe_gt *gt, u32 vfid); -void xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); +int xe_gt_sriov_pf_monitor_print_events(struct xe_gt *gt, struct drm_printer *p); #ifdef CONFIG_PCI_IOV int xe_gt_sriov_pf_monitor_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len); From 926a08cf19be32f9c9e25e0b00cc7f211f4f85a3 Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Fri, 24 Apr 2026 10:50:35 +0100 Subject: [PATCH 317/697] wifi: ath11k: fix peer resolution on rx path when peer_id=0 [ Upstream commit 2a2451a34afdf563b3102d36a4b6cf335cf813e2 ] It has been observed that on certain chipsets a peer can be assigned peer_id=0. For reception of non-aggregated MPDUs this is fine as ath11k_dp_rx_h_find_peer() has a fallback case where it locates the peer based upon the source MAC address. On an aggregated link, the mpdu_start header is only populated by hardware on the first sub-MSDU. This causes the peer resolution to be skipped for the subsequent MSDUs and the encryption type of these frames to be set to an incorrect value, resulting in these MSDUs being dropped by ieee80211. ath11k_pci 0000:03:00.0: data rx skb 000000002f4b704d len 1534 peer xx:xx:xx:xx:xx:xx 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d1a fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 1 last_msdu 0 ath11k_pci 0000:03:00.0: data rx skb 0000000038acd580 len 1534 peer (null) 0 ucast sn 3063 he160 rate_idx 9 vht_nss 2 freq 5240 band 1 flag 0x40d00 fcs-err 0 mic-err 0 amsdu-more 0 peer_id 0 first_msdu 0 last_msdu 1 Remove the null peer_id checks in ath11k_dp_rx_h_find_peer() and ath11k_hal_rx_parse_mon_status_tlv(), allowing peers with an assigned ID of 0 to be resolved. Tested-on: QCA2066 hw2.1 PCI WLAN.HSP.1.1-03926.13-QCAHSPSWPL_V2_SILICONZ_CE-2.52297.9 Fixes: 2167fa606c0f ("ath11k: Add support for RX decapsulation offload") Reviewed-by: Baochen Qiang Signed-off-by: Matthew Leach Reviewed-by: P Praneesh Link: https://patch.msgid.link/20260424-ath11k-null-peerid-workaround-v4-1-252b224d3cf6@collabora.com Signed-off-by: Jeff Johnson Signed-off-by: Sasha Levin --- drivers/net/wireless/ath/ath11k/dp_rx.c | 3 +-- drivers/net/wireless/ath/ath11k/hal_rx.c | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 44eea682c297..5666f6647445 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -2214,8 +2214,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu) lockdep_assert_held(&ab->base_lock); - if (rxcb->peer_id) - peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); + peer = ath11k_peer_find_by_id(ab, rxcb->peer_id); if (peer) return peer; diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 753bd93f0212..51e0840bc0d1 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab, case HAL_RX_MPDU_START: { struct hal_rx_mpdu_info *mpdu_info = (struct hal_rx_mpdu_info *)tlv_data; - u16 peer_id; - peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); - if (peer_id) - ppdu_info->peer_id = peer_id; + ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info); break; } case HAL_RXPCU_PPDU_END_INFO: { From 8ea34da689646a1261445e1e40f25bdee2df14a6 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 29 Apr 2026 11:59:01 +0200 Subject: [PATCH 318/697] drm/mediatek: mtk_cec: Fix non-static global variable [ Upstream commit 571f00a5fb725984049bd532ee8193cc34ff2994 ] The struct 'mtk_cec_driver' is not used outside of the mtk_cec.c file, so make it static to silence sparse warning: ``` drivers/gpu/drm/mediatek/mtk_cec.c:243:24: sparse: warning: symbol 'mtk_cec_driver' was not declared. Should it be static? ``` Fixes: 1e914a89ab7e ("drm/mediatek: mtk_cec: Switch to register as module_platform_driver") Signed-off-by: Louis-Alexis Eyraud Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-3-d95c4d118b83@collabora.com/ Signed-off-by: Chun-Kuang Hu Signed-off-by: Sasha Levin --- drivers/gpu/drm/mediatek/mtk_cec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c index c7be530ca041..b8ccd6e55bed 100644 --- a/drivers/gpu/drm/mediatek/mtk_cec.c +++ b/drivers/gpu/drm/mediatek/mtk_cec.c @@ -240,7 +240,7 @@ static const struct of_device_id mtk_cec_of_ids[] = { }; MODULE_DEVICE_TABLE(of, mtk_cec_of_ids); -struct platform_driver mtk_cec_driver = { +static struct platform_driver mtk_cec_driver = { .probe = mtk_cec_probe, .remove = mtk_cec_remove, .driver = { From 83b8a0f72ecc08bf71655a49b94fd69f7f06b5f1 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 29 Apr 2026 11:59:02 +0200 Subject: [PATCH 319/697] drm/mediatek: mtk_hdmi_ddc: Fix non-static global variable [ Upstream commit 87ed4e845d5a90bba1a56c0a5c580a13982e8648 ] The struct 'mtk_hdmi_ddc_driver' is not used outside of the mtk_hdmi_ddc.c file, so make it static to silence sparse warning: ``` drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:331:24: sparse: warning: symbol 'mtk_hdmi_ddc_driver' was not declared. Should it be static? ``` Fixes: c241118b6216 ("drm/mediatek: mtk_hdmi_ddc: Switch to register as module_platform_driver") Signed-off-by: Louis-Alexis Eyraud Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/dri-devel/patch/20260429-mediatek-drm-fix-sparse-warnings-v1-4-d95c4d118b83@collabora.com/ Signed-off-by: Chun-Kuang Hu Signed-off-by: Sasha Levin --- drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c index 6358e1af69b4..2acbdb025d89 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c @@ -328,7 +328,7 @@ static const struct of_device_id mtk_hdmi_ddc_match[] = { }; MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match); -struct platform_driver mtk_hdmi_ddc_driver = { +static struct platform_driver mtk_hdmi_ddc_driver = { .probe = mtk_hdmi_ddc_probe, .remove = mtk_hdmi_ddc_remove, .driver = { From 6a01413a4e8fcb0263d7bef5075c5f8f4eb3a8b6 Mon Sep 17 00:00:00 2001 From: Qing Ming Date: Sat, 16 May 2026 15:08:49 +0800 Subject: [PATCH 320/697] cgroup/rstat: validate cpu before css_rstat_cpu() access [ Upstream commit 8817005efbdfdf5d4e4814cb5dc52b53d12917d7 ] css_rstat_updated() is exposed as a BPF kfunc and accepts a caller-provided cpu argument. The function uses cpu for per-cpu rstat lookups without checking whether it refers to a valid possible CPU. A BPF iter/cgroup program with CAP_BPF and CAP_PERFMON can pass an invalid cpu value. On an unfixed UBSCAN_BOUNDS test kernel, cpu == 0x7fffffff triggers: UBSAN: array-index-out-of-bounds in kernel/cgroup/rstat.c:31:9 index 2147483647 is out of range for type 'long unsigned int [64]' Call Trace: css_rstat_updated bpf_iter_run_prog cgroup_iter_seq_show bpf_seq_read Add cpu validation to the BPF-facing css_rstat_updated() kfunc and move the common implementation to __css_rstat_updated() for in-kernel callers. Fixes: a319185be9f5 ("cgroup: bpf: enable bpf programs to integrate with rstat") Signed-off-by: Qing Ming Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- block/blk-cgroup.c | 2 +- include/linux/cgroup.h | 1 + kernel/cgroup/rstat.c | 30 ++++++++++++++++++++---------- mm/memcontrol.c | 6 +++--- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 9a8d047be580..f1ea69743c54 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -2241,7 +2241,7 @@ void blk_cgroup_bio_start(struct bio *bio) } u64_stats_update_end_irqrestore(&bis->sync, flags); - css_rstat_updated(&blkcg->css, cpu); + __css_rstat_updated(&blkcg->css, cpu); put_cpu(); } diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 6ed477338b16..7b2807009155 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -713,6 +713,7 @@ static inline void cgroup_path_from_kernfs_id(u64 id, char *buf, size_t buflen) /* * cgroup scalable recursive statistics. */ +void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu); void css_rstat_updated(struct cgroup_subsys_state *css, int cpu); void css_rstat_flush(struct cgroup_subsys_state *css); diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index 150e5871e66f..ed60ba119c68 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include "cgroup-internal.h" +#include #include #include @@ -53,7 +54,7 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) } /** - * css_rstat_updated - keep track of updated rstat_cpu + * __css_rstat_updated - keep track of updated rstat_cpu * @css: target cgroup subsystem state * @cpu: cpu on which rstat_cpu was updated * @@ -63,20 +64,17 @@ static inline struct llist_head *ss_lhead_cpu(struct cgroup_subsys *ss, int cpu) * * NOTE: if the user needs the guarantee that the updater either add itself in * the lockless list or the concurrent flusher flushes its updated stats, a - * memory barrier is needed before the call to css_rstat_updated() i.e. a + * memory barrier is needed before the call to __css_rstat_updated() i.e. a * barrier after updating the per-cpu stats and before calling - * css_rstat_updated(). + * __css_rstat_updated(). */ -__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) +void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) { struct llist_head *lhead; struct css_rstat_cpu *rstatc; struct llist_node *self; - /* - * Since bpf programs can call this function, prevent access to - * uninitialized rstat pointers. - */ + /* Prevent access to uninitialized rstat pointers. */ if (!css_uses_rstat(css)) return; @@ -125,6 +123,18 @@ __bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) llist_add(&rstatc->lnode, lhead); } +/* + * BPF-facing wrapper for __css_rstat_updated(). Validate the caller-provided + * CPU before passing it to the internal rstat updater. + */ +__bpf_kfunc void css_rstat_updated(struct cgroup_subsys_state *css, int cpu) +{ + if (unlikely(cpu < 0 || cpu >= nr_cpu_ids || !cpu_possible(cpu))) + return; + + __css_rstat_updated(css, cpu); +} + static void __css_process_update_tree(struct cgroup_subsys_state *css, int cpu) { /* put @css and all ancestors on the corresponding updated lists */ @@ -170,7 +180,7 @@ static void css_process_update_tree(struct cgroup_subsys *ss, int cpu) * flusher flush the stats updated by the updater who have * observed that they are already on the list. The * corresponding barrier pair for this one should be before - * css_rstat_updated() by the user. + * __css_rstat_updated() by the user. * * For now, there aren't any such user, so not adding the * barrier here but if such a use-case arise, please add @@ -614,7 +624,7 @@ static void cgroup_base_stat_cputime_account_end(struct cgroup *cgrp, unsigned long flags) { u64_stats_update_end_irqrestore(&rstatbc->bsync, flags); - css_rstat_updated(&cgrp->self, smp_processor_id()); + __css_rstat_updated(&cgrp->self, smp_processor_id()); put_cpu_ptr(rstatbc); } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 61cf6af26f3c..4df68e5468ad 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -571,7 +571,7 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, if (!val) return; - css_rstat_updated(&memcg->css, cpu); + __css_rstat_updated(&memcg->css, cpu); statc_pcpu = memcg->vmstats_percpu; for (; statc_pcpu; statc_pcpu = statc->parent_pcpu) { statc = this_cpu_ptr(statc_pcpu); @@ -2525,7 +2525,7 @@ static inline void account_slab_nmi_safe(struct mem_cgroup *memcg, struct mem_cgroup_per_node *pn = memcg->nodeinfo[pgdat->node_id]; /* preemption is disabled in_nmi(). */ - css_rstat_updated(&memcg->css, smp_processor_id()); + __css_rstat_updated(&memcg->css, smp_processor_id()); if (idx == NR_SLAB_RECLAIMABLE_B) atomic_add(nr, &pn->slab_reclaimable); else @@ -2749,7 +2749,7 @@ static inline void account_kmem_nmi_safe(struct mem_cgroup *memcg, int val) mod_memcg_state(memcg, MEMCG_KMEM, val); } else { /* preemption is disabled in_nmi(). */ - css_rstat_updated(&memcg->css, smp_processor_id()); + __css_rstat_updated(&memcg->css, smp_processor_id()); atomic_add(val, &memcg->kmem_stat); } } From 0010296879dfec8e9f1aa8292510b03499370210 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Fri, 15 May 2026 11:24:11 -0700 Subject: [PATCH 321/697] ice: ptp: serialize E825 PHY timer start with PTP lock [ Upstream commit 781ff8f2d575a794a2a4f11605288ae06757f5eb ] ice_start_phy_timer_eth56g() programs TIMETUS registers and issues INIT_INCVAL without holding the global PTP semaphore. This allows concurrent PTP command paths to interleave with PHY timer start, which can make the sequence fail and leave timer initialization inconsistent. Take the PTP lock around TIMETUS registers programming and INIT_INCVAL command execution, and make sure the lock is released on all error paths. Keep the subsequent sync step outside of this critical section, since ice_sync_phy_timer_eth56g() takes the same semaphore internally. Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products") Reviewed-by: Arkadiusz Kubalewski Signed-off-by: Grzegorz Nitka Reviewed-by: Aleksandr Loktionov Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-5-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 30b783939816..0f378e68f72f 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -2141,16 +2141,23 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) } incval = (u64)hi << 32 | lo; + if (!ice_ptp_lock(hw)) { + dev_err(ice_hw_to_dev(hw), "Failed to acquire PTP semaphore\n"); + return -EBUSY; + } + err = ice_write_40b_ptp_reg_eth56g(hw, port, PHY_REG_TIMETUS_L, incval); if (err) - return err; + goto err_ptp_unlock; err = ice_ptp_one_port_cmd(hw, port, ICE_PTP_INIT_INCVAL); if (err) - return err; + goto err_ptp_unlock; ice_ptp_exec_tmr_cmd(hw); + ice_ptp_unlock(hw); + err = ice_sync_phy_timer_eth56g(hw, port); if (err) return err; @@ -2166,6 +2173,10 @@ int ice_start_phy_timer_eth56g(struct ice_hw *hw, u8 port) ice_debug(hw, ICE_DBG_PTP, "Enabled clock on PHY port %u\n", port); return 0; + +err_ptp_unlock: + ice_ptp_unlock(hw); + return err; } /** From 89964ddb322a65979ef867e240915223352922e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Nitka Date: Fri, 15 May 2026 11:24:12 -0700 Subject: [PATCH 322/697] ice: ptp: use primary NAC semaphore on E825 [ Upstream commit 7b28523546c7e4adbb8436f2986efcfc8382985e ] For E825 2xNAC configurations, PTP semaphore operations must hit the primary NAC register block so both sides coordinate on the same lock. Commit e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 devices") updated other primary-only PTP register accesses to use the primary NAC on non-primary functions, but left ice_ptp_lock() and ice_ptp_unlock() operating on the local NAC. As a result, secondary NAC PTP paths can take a different semaphore than the primary side. Select the primary hardware in ice_ptp_lock() and ice_ptp_unlock() when the current function is not primary, keeping semaphore operations symmetric and consistent with the rest of the 2xNAC PTP register access path. Fixes: e2193f9f9ec9 ("ice: enable timesync operation on 2xNAC E825 devices") Reviewed-by: Arkadiusz Kubalewski Signed-off-by: Grzegorz Nitka Reviewed-by: Aleksandr Loktionov Tested-by: Alexander Nowlin Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-6-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c index 0f378e68f72f..99bf38cf352a 100644 --- a/drivers/net/ethernet/intel/ice/ice_ptp_hw.c +++ b/drivers/net/ethernet/intel/ice/ice_ptp_hw.c @@ -5264,9 +5264,13 @@ static void ice_ptp_init_phy_e830(struct ice_ptp_hw *ptp) */ bool ice_ptp_lock(struct ice_hw *hw) { + struct ice_pf *pf = container_of(hw, struct ice_pf, hw); u32 hw_lock; int i; + if (!ice_is_primary(hw)) + hw = ice_get_primary_hw(pf); + #define MAX_TRIES 15 for (i = 0; i < MAX_TRIES; i++) { @@ -5293,6 +5297,11 @@ bool ice_ptp_lock(struct ice_hw *hw) */ void ice_ptp_unlock(struct ice_hw *hw) { + struct ice_pf *pf = container_of(hw, struct ice_pf, hw); + + if (!ice_is_primary(hw)) + hw = ice_get_primary_hw(pf); + wr32(hw, PFTSYN_SEM + (PFTSYN_SEM_BYTES * hw->pf_id), 0); } From 1f83545f432d106d5fc71d3997b2d382104ebcc4 Mon Sep 17 00:00:00 2001 From: Kohei Enju Date: Fri, 15 May 2026 11:24:15 -0700 Subject: [PATCH 323/697] igc: set tx buffer type for SMD frames [ Upstream commit 5acc641e590e008caaed480ed9ffae47cf7ecbdf ] Sashiko pointed out that igc_fpe_init_smd_frame() initializes igc_tx_buffer fields for an SMD skb, but does not set the buffer type: https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp Since igc_tx_buffer entries are reused, a stale XDP or XSK type can remain and make TX completion use the wrong cleanup path. Set the buffer type to IGC_TX_BUFFER_TYPE_SKB. Fixes: 5422570c0010 ("igc: add support for frame preemption verification") Signed-off-by: Kohei Enju Reviewed-by: Aleksandr Loktionov Reviewed-by: Simon Horman Tested-by: Avigail Dahan Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260515182419.1597859-9-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/intel/igc/igc_tsn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 02dd9f0290a3..52de2bcbadbe 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -34,6 +34,7 @@ static int igc_fpe_init_smd_frame(struct igc_ring *ring, return -ENOMEM; } + buffer->type = IGC_TX_BUFFER_TYPE_SKB; buffer->skb = skb; buffer->protocol = 0; buffer->bytecount = skb->len; From ad8e3d096fa1e2f8b1009731c6e0cdae7ebedf79 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Mon, 11 May 2026 18:02:15 +0530 Subject: [PATCH 324/697] drm/i915/dp: Fix readback for target_rr in Adaptive Sync SDP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f87abd0c6604fb6cc31cc86fc7ccc6a576924352 ] Correct the bit-shift logic to properly readback the 10 bit target_rr from DB3 and DB4. v2: Align the style with readback for vtotal. (Ville) Fixes: 12ea89291603 ("drm/i915/dp: Add Read/Write support for Adaptive Sync SDP") Cc: Mitul Golani Cc: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Reviewed-by: Ville Syrjälä Link: https://patch.msgid.link/20260511123218.1589830-2-ankit.k.nautiyal@intel.com (cherry picked from commit f7abc4af2b19240a145a221461dfe756cc01d74a) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 61f22275403b..a44fbac1e5e2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4899,7 +4899,7 @@ int intel_dp_as_sdp_unpack(struct drm_dp_as_sdp *as_sdp, as_sdp->length = sdp->sdp_header.HB3 & DP_ADAPTIVE_SYNC_SDP_LENGTH; as_sdp->mode = sdp->db[0] & DP_ADAPTIVE_SYNC_SDP_OPERATION_MODE; as_sdp->vtotal = (sdp->db[2] << 8) | sdp->db[1]; - as_sdp->target_rr = (u64)sdp->db[3] | ((u64)sdp->db[4] & 0x3); + as_sdp->target_rr = ((sdp->db[4] & 0x3) << 8) | sdp->db[3]; as_sdp->target_rr_divider = sdp->db[4] & 0x20 ? true : false; return 0; From f1739debda62d8ed591ec8635857265ca6eef77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20J=C3=A4gersk=C3=BCpper?= Date: Fri, 15 May 2026 23:58:45 +0200 Subject: [PATCH 325/697] kbuild: pacman-pkg: make "rc" releases adhere to pacman versioning scheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 202550713128da20d9381d6d2dc0f6b73839f434 ] The package versioning scheme does not enable smooth upgrades from "rc" releases to the corresponding stable releases (e.g. 7.0.0-rc7 -> 7.0.0) because pacman considers that a downgrade due to the underscore in pkgver (e.g. 7.0.0_rc7), see e.g. vercmp(8) for an explanation of the package version comparison used by pacman. Package versions which are derived from said releases (e.g. built from git revisions) are similarly affected. Fix this by modifying pkgver in order to remove the hyphen from kernel versions containing "-rcN", where N is a non-negative integer. Acked-by: Thomas Weißschuh Signed-off-by: Viktor Jägersküpper Reviewed-by: Nathan Chancellor Tested-by: Nathan Chancellor Link: https://patch.msgid.link/20260515215913.92481-1-viktor_jaegerskuepper@freenet.de Fixes: c8578539deba ("kbuild: add script and target to generate pacman package") Signed-off-by: Nicolas Schier Signed-off-by: Sasha Levin --- scripts/package/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package/PKGBUILD b/scripts/package/PKGBUILD index 452374d63c24..1213c8e04671 100644 --- a/scripts/package/PKGBUILD +++ b/scripts/package/PKGBUILD @@ -10,7 +10,7 @@ for pkg in $_extrapackages; do pkgname+=("${pkgbase}-${pkg}") done -pkgver="${KERNELRELEASE//-/_}" +pkgver="$(echo "${KERNELRELEASE}" | sed 's/-\(rc[0-9]\+\)/\1/;s/-/_/g')" # The PKGBUILD is evaluated multiple times. # Running scripts/build-version from here would introduce inconsistencies. pkgrel="${KBUILD_REVISION}" From 89bed786f23182fdf975668538bfee9df38aaaee Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 14 May 2026 15:04:21 +0100 Subject: [PATCH 326/697] net: dsa: mt7530: fix FDB entries not aging out with short timeout [ Upstream commit e824e40d0e841fab66ab7897d6c7b14dc81c66a7 ] The DSA forwarding selftests bridge_vlan_aware.sh and bridge_vlan_unaware.sh configure the bridge with ageing_time set to LOW_AGEING_TIME (1000 centiseconds, i.e. 10 seconds) and then run learning_test() in lib.sh, which expects a learned FDB entry to be removed after ageing_time + 10 seconds. On MT7530/MT7531 the entry persisted past the deadline and the "Found FDB record when should not" assertion failed. With msecs=10000, the algorithm in mt7530_set_ageing_time() finds AGE_CNT=0 and AGE_UNIT=9 as the first exact match (starting the search from tmp_age_count=0). The per-entry aging counter is initialized to AGE_CNT when a MAC address is learned, so with AGE_CNT=0 new entries start with a counter value of 0, which the hardware treats as "already aged" and never removes, effectively disabling aging. Fix this by starting the search from tmp_age_count=1 to ensure entries always have a non-zero initial aging counter. For a 10-second ageing time this yields AGE_CNT=1 and AGE_UNIT=4 instead: the timer ticks every 5 seconds and entries are removed after 2 ticks. Starting the search at AGE_CNT=1 raises the minimum representable ageing time from 1 to 2 seconds. Without bounds, a stale ageing_time of 1 second would now make the loop fall through without setting age_count and age_unit, leaving them uninitialized when written to the MT7530_AAC hardware register. Set ds->ageing_time_min and ds->ageing_time_max so the DSA core validates the range before the callback is invoked, and drop the now-redundant range check from mt7530_set_ageing_time(). Fixes: ea6d5c924e39 ("net: dsa: mt7530: support setting ageing time") Signed-off-by: Daniel Golle Link: https://patch.msgid.link/7788ded12dc07b1bce329ec35fa70f4b45f3f9b7.1778766629.git.daniel@makrotopia.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 548b85befbf4..d6ce335f6688 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -973,12 +973,16 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) unsigned int age_count; unsigned int age_unit; - /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds */ - if (secs < 1 || secs > (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1)) - return -ERANGE; - - /* iterate through all possible age_count to find the closest pair */ - for (tmp_age_count = 0; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { + /* Applied timer is (AGE_CNT + 1) * (AGE_UNIT + 1) seconds. + * The DSA core has already validated the range using + * ds->ageing_time_min and ds->ageing_time_max. + * + * Iterate through all possible age_count values to find the closest + * pair. Start from 1 because the per-entry aging counter is + * initialized to AGE_CNT and a value of 0 means the entry will + * never be aged out. + */ + for (tmp_age_count = 1; tmp_age_count <= AGE_CNT_MAX; ++tmp_age_count) { unsigned int tmp_age_unit = secs / (tmp_age_count + 1) - 1; if (tmp_age_unit <= AGE_UNIT_MAX) { @@ -2378,6 +2382,8 @@ mt7530_setup(struct dsa_switch *ds) ds->assisted_learning_on_cpu_port = true; ds->mtu_enforcement_ingress = true; + ds->ageing_time_min = 2 * 1000; + ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; if (priv->id == ID_MT7530) { regulator_set_voltage(priv->core_pwr, 1000000, 1000000); @@ -2567,6 +2573,8 @@ mt7531_setup_common(struct dsa_switch *ds) ds->assisted_learning_on_cpu_port = true; ds->mtu_enforcement_ingress = true; + ds->ageing_time_min = 2 * 1000; + ds->ageing_time_max = (AGE_CNT_MAX + 1) * (AGE_UNIT_MAX + 1) * 1000; mt753x_trap_frames(priv); From f71fc35b5e45973bf62543f6e91a8cceae6bfb3b Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Thu, 14 May 2026 15:04:35 +0100 Subject: [PATCH 327/697] net: dsa: mt7530: preserve VLAN tags on trapped link-local frames [ Upstream commit 3ac85bcfd404b588298c95c6fba8aad4ad334f57 ] The BPC, RGAC1 and RGAC2 registers control the handling of link-local frames with reserved MAC DAs (01:80:C2:00:00:0x). These frames are correctly trapped to the CPU port, but the egress VLAN tag attribute was set to MT7530_VLAN_EG_UNTAGGED which causes the switch to strip any VLAN tags from trapped frames before they reach the CPU. This causes VLAN-tagged link-local frames (STP BPDUs, LLDP, PTP Peer Delay Requests) to arrive at the CPU without their VLAN tag, so they are delivered to the base network interface instead of the VLAN sub-interface. The DSA local_termination selftest confirms this: all link-local protocol tests on VLAN upper interfaces fail. Set the EG_TAG attribute to MT7530_VLAN_EG_DISABLED (system default) so that the switch does not modify VLAN tags in trapped frames. This way VLAN-tagged frames retain their original tag and are delivered to the correct VLAN sub-interface, matching the behavior of non-trapped frames which pass through without VLAN tag modification. Fixes: 69ddba9d170b ("net: dsa: mt7530: fix handling of all link-local frames") Signed-off-by: Daniel Golle Acked-by: Chester A. Unal Link: https://patch.msgid.link/891e0cd34db2a5fe20ceb73283a81fb5f71427ca.1778766629.git.daniel@makrotopia.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/dsa/mt7530.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index d6ce335f6688..4571da0d7a8f 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1250,37 +1250,40 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) static void mt753x_trap_frames(struct mt7530_priv *priv) { - /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them - * VLAN-untagged. + /* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress + * them with the EG_TAG attribute set to disabled (system default) + * so that any VLAN tags in the frame are not modified by the + * switch egress VLAN tag processing. This preserves VLAN tags + * for reception on VLAN sub-interfaces. */ mt7530_rmw(priv, MT753X_BPC, PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, - PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | - BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | TO_CPU_FW_CPU_ONLY); - /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress - * them VLAN-untagged. + /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and + * egress them with EG_TAG disabled. */ mt7530_rmw(priv, MT753X_RGAC1, R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, - R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | - R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | TO_CPU_FW_CPU_ONLY); - /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress - * them VLAN-untagged. + /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and + * egress them with EG_TAG disabled. */ mt7530_rmw(priv, MT753X_RGAC2, R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, - R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | - R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | + R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | TO_CPU_FW_CPU_ONLY); } From 09ec063d87c2dd3fa6f3561361a017bd882e9f37 Mon Sep 17 00:00:00 2001 From: Erni Sri Satya Vennela Date: Thu, 14 May 2026 12:41:51 -0700 Subject: [PATCH 328/697] net: mana: Fix TOCTOU double-fetch of hwc_msg_id from DMA buffer [ Upstream commit 35f0f0a2536a4d604b4dbad92c85c4a8fdebb870 ] In mana_hwc_rx_event_handler(), resp->response.hwc_msg_id is read from DMA-coherent memory and bounds-checked, then mana_hwc_handle_resp() re-reads the same field from the same DMA buffer for test_bit() and pointer arithmetic. DMA-coherent memory is mapped uncacheable on x86 and is shared, unencrypted, in Confidential VMs (SEV-SNP/TDX), so each load goes directly to host-visible memory. A H/W can modify the value between the check and the use, bypassing the bounds validation. Fix this by reading hwc_msg_id exactly once using READ_ONCE() into a stack-local variable in mana_hwc_rx_event_handler(), and passing the validated value as a parameter to mana_hwc_handle_resp(). Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Erni Sri Satya Vennela Link: https://patch.msgid.link/20260514194156.466823-1-ernis@linux.microsoft.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- .../net/ethernet/microsoft/mana/hw_channel.c | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 840c6b8957c9..1986bf493399 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -77,21 +77,19 @@ static int mana_hwc_post_rx_wqe(const struct hwc_wq *hwc_rxq, } static void mana_hwc_handle_resp(struct hw_channel_context *hwc, u32 resp_len, - struct hwc_work_request *rx_req) + struct hwc_work_request *rx_req, u16 msg_id) { const struct gdma_resp_hdr *resp_msg = rx_req->buf_va; struct hwc_caller_ctx *ctx; int err; - if (!test_bit(resp_msg->response.hwc_msg_id, - hwc->inflight_msg_res.map)) { - dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", - resp_msg->response.hwc_msg_id); + if (!test_bit(msg_id, hwc->inflight_msg_res.map)) { + dev_err(hwc->dev, "hwc_rx: invalid msg_id = %u\n", msg_id); mana_hwc_post_rx_wqe(hwc->rxq, rx_req); return; } - ctx = hwc->caller_ctx + resp_msg->response.hwc_msg_id; + ctx = hwc->caller_ctx + msg_id; err = mana_hwc_verify_resp_msg(ctx, resp_msg, resp_len); if (err) goto out; @@ -251,6 +249,7 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, struct gdma_sge *sge; u64 rq_base_addr; u64 rx_req_idx; + u16 msg_id; u8 *wqe; if (WARN_ON_ONCE(hwc_rxq->gdma_wq->id != gdma_rxq_id)) @@ -269,13 +268,17 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; resp = (struct gdma_resp_hdr *)rx_req->buf_va; - if (resp->response.hwc_msg_id >= hwc->num_inflight_msg) { - dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", - resp->response.hwc_msg_id); + /* Read msg_id once from DMA buffer to prevent TOCTOU: + * DMA memory is shared/unencrypted in CVMs - host can + * modify it between reads. + */ + msg_id = READ_ONCE(resp->response.hwc_msg_id); + if (msg_id >= hwc->num_inflight_msg) { + dev_err(hwc->dev, "HWC RX: wrong msg_id=%u\n", msg_id); return; } - mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req); + mana_hwc_handle_resp(hwc, rx_oob->tx_oob_data_size, rx_req, msg_id); /* Can no longer use 'resp', because the buffer is posted to the HW * in mana_hwc_handle_resp() above. From 098419a4b0623a195ab1d88cfebf4a9f96a7598d Mon Sep 17 00:00:00 2001 From: Oliver White Date: Thu, 9 Apr 2026 15:43:47 +1200 Subject: [PATCH 329/697] platform/surface: aggregator_registry: omit battery & AC nodes on Surface Laptop 7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 0488073a6c84571dd3cffe581a4a73a5fceb099d ] Surface Laptop 7 exposes battery and AC status via Qualcomm PMIC GLINK qcom_battmgr. Registering the standard SSAM battery and AC client devices on this platform causes duplicate power-supply devices to appear. Drop the SSAM battery and AC nodes from the Surface Laptop 7 registry group so that only the qcom_battmgr power supplies are instantiated. Fixes: b27622f13172 ("platform/surface: Add OF support") Signed-off-by: Oliver White Link: https://patch.msgid.link/20260409034347.17381-1-oliverjwhite07@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/surface/surface_aggregator_registry.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index a594d5fcfcfd..d29158faba3e 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -295,8 +295,6 @@ static const struct software_node *ssam_node_group_sl6[] = { /* Devices for Surface Laptop 7. */ static const struct software_node *ssam_node_group_sl7[] = { &ssam_node_root, - &ssam_node_bat_ac, - &ssam_node_bat_main, &ssam_node_tmp_perf_profile_with_fan, &ssam_node_fan_speed, &ssam_node_hid_sam_keyboard, From 7ea5aad8d351a8791b86f49bdc39ec67e277c35c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 17:11:49 +0200 Subject: [PATCH 330/697] platform/x86: adv_swbutton: Check ACPI_HANDLE() against NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit e7a9a6ea40e352cd7977f6a8c80bdeadf65ad838 ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_HANDLE() check against NULL to the platform/x86 adv_swbutton driver. Fixes: 3d904005f686 ("platform/x86: add support for Advantech software defined button") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/5115425.31r3eYUQgx@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/adv_swbutton.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/adv_swbutton.c b/drivers/platform/x86/adv_swbutton.c index 6fa60f3fc53c..8f7a26e6de81 100644 --- a/drivers/platform/x86/adv_swbutton.c +++ b/drivers/platform/x86/adv_swbutton.c @@ -48,10 +48,14 @@ static int adv_swbutton_probe(struct platform_device *device) { struct adv_swbutton *button; struct input_dev *input; - acpi_handle handle = ACPI_HANDLE(&device->dev); + acpi_handle handle; acpi_status status; int error; + handle = ACPI_HANDLE(&device->dev); + if (!handle) + return -ENODEV; + button = devm_kzalloc(&device->dev, sizeof(*button), GFP_KERNEL); if (!button) return -ENOMEM; From ed864a7b881c82fd2ce74695afc3fccfab6fbe15 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 17:12:40 +0200 Subject: [PATCH 331/697] platform/x86: hp_accel: Check ACPI_COMPANION() against NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit abfbe5ee8ae89f1f5449790423d5dd3e423545bd ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_COMPANION() check against NULL to the platform/x86 hp_accel driver. Fixes: 8ebcb6c94c71 ("platform/x86: hp_accel: Convert to be a platform driver") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/2425918.ElGaqSPkdT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/hp/hp_accel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/hp/hp_accel.c b/drivers/platform/x86/hp/hp_accel.c index 10d5af18d639..39b73dc473f1 100644 --- a/drivers/platform/x86/hp/hp_accel.c +++ b/drivers/platform/x86/hp/hp_accel.c @@ -300,6 +300,9 @@ static int lis3lv02d_probe(struct platform_device *device) int ret; lis3_dev.bus_priv = ACPI_COMPANION(&device->dev); + if (!lis3_dev.bus_priv) + return -ENODEV; + lis3_dev.init = lis3lv02d_acpi_init; lis3_dev.read = lis3lv02d_acpi_read; lis3_dev.write = lis3lv02d_acpi_write; From f6dfd64bfd9b20e7adda533ed3cf3bed528ae4e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 17:13:28 +0200 Subject: [PATCH 332/697] platform/x86: intel-hid: Check ACPI_HANDLE() against NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 5c69e090ae5dd93d910f70db0796357080707d26 ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_HANDLE() check against NULL to the platform/x86 intel-hid driver. Fixes: ecc83e52b28c ("intel-hid: new hid event driver for hotkeys") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/1971512.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/hid.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index c5e80887d0cb..f7e358be1af3 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -682,12 +682,16 @@ static bool button_array_present(struct platform_device *device) static int intel_hid_probe(struct platform_device *device) { - acpi_handle handle = ACPI_HANDLE(&device->dev); unsigned long long mode, dummy; struct intel_hid_priv *priv; + acpi_handle handle; acpi_status status; int err; + handle = ACPI_HANDLE(&device->dev); + if (!handle) + return -ENODEV; + intel_hid_init_dsm(handle); if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_HDMM_FN, &mode)) { From 09deb063eecf43b91b04401f7452c58f1b9cd453 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 12 May 2026 17:16:22 +0200 Subject: [PATCH 333/697] platform/x86: intel-vbtn: Check ACPI_HANDLE() against NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit a9f305c5a355efeb240d406d378491d9eec02d07 ] Every platform driver can be forced to match a device that doesn't match its list of device IDs because of device_match_driver_override(), so platform drivers that rely on the existence of a device's ACPI companion object need to verify its presence. Accordingly, add a requisite ACPI_HANDLE() check against NULL to the platform/x86 intel-vbtn driver. Fixes: 26173179fae1 ("platform/x86: intel-vbtn: Eval VBDL after registering our notifier") Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/3426431.aeNJFYEL58@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin --- drivers/platform/x86/intel/vbtn.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c index 232cd12e3c9f..03b5bf461a86 100644 --- a/drivers/platform/x86/intel/vbtn.c +++ b/drivers/platform/x86/intel/vbtn.c @@ -275,12 +275,16 @@ static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel) static int intel_vbtn_probe(struct platform_device *device) { - acpi_handle handle = ACPI_HANDLE(&device->dev); bool dual_accel, has_buttons, has_switches; struct intel_vbtn_priv *priv; + acpi_handle handle; acpi_status status; int err; + handle = ACPI_HANDLE(&device->dev); + if (!handle) + return -ENODEV; + dual_accel = dual_accel_detect(); has_buttons = acpi_has_method(handle, "VBDL"); has_switches = intel_vbtn_has_switches(handle, dual_accel); From d5b11e15ee676438a93a45b854a19b2177011084 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 19 May 2026 12:40:24 +0700 Subject: [PATCH 334/697] ASoC: soc-utils: Add missing va_end in snd_soc_ret() [ Upstream commit 298a43b54432fbc3a32949a94c72544ee18c8c00 ] The default case in snd_soc_ret() use va_start without va_end to cleanup "args" object which can cause undefined behavior. So, add missing va_end to cleanup "args" object. This is reported by Coverity Scan as "Missing varargs init or cleanup". Fixes: 943116ba2a6a ("ASoC: add common snd_soc_ret() and use it") Signed-off-by: Robertus Diawan Chris Link: https://patch.msgid.link/20260519054024.274741-1-robertusdchris@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/soc-utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c index c8adfff826bd..9cb7567e263e 100644 --- a/sound/soc/soc-utils.c +++ b/sound/soc/soc-utils.c @@ -36,6 +36,7 @@ int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...) vaf.va = &args; dev_err(dev, "ASoC error (%d): %pV", ret, &vaf); + va_end(args); } return ret; From 8c63698737b4bdf333c3a2920f29988ee06eb19e Mon Sep 17 00:00:00 2001 From: Shiraz Saleem Date: Tue, 12 May 2026 02:42:09 -0700 Subject: [PATCH 335/697] RDMA/mana_ib: Report max_msg_sz in mana_ib_query_port [ Upstream commit c9a40f6531b81baa9619bcc2697ff86896afcce7 ] Report max_msg_sz for mana_ib, which is 16MB. Fixes: 4bda1d5332ec ("RDMA/mana_ib: Implement port parameters") Signed-off-by: Shiraz Saleem Signed-off-by: Konstantin Taranov Link: https://patch.msgid.link/20260512094209.264955-1-kotaranov@linux.microsoft.com Reviewed-by: Long Li Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/hw/mana/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/infiniband/hw/mana/main.c b/drivers/infiniband/hw/mana/main.c index fac159f7128d..4143be70eea2 100644 --- a/drivers/infiniband/hw/mana/main.c +++ b/drivers/infiniband/hw/mana/main.c @@ -639,6 +639,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port, if (mana_ib_is_rnic(dev)) { props->gid_tbl_len = 16; props->ip_gids = true; + props->max_msg_sz = SZ_16M; if (port == 1) props->port_cap_flags = IB_PORT_CM_SUP; } From eae62c5451e67e8b033c1681fd3b85d7e9a9a28f Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Thu, 14 May 2026 19:38:34 +0800 Subject: [PATCH 336/697] RDMA/rtrs: Fix use-after-free in path file creation cleanup [ Upstream commit 5b74373390113fba798a76b483837029ab010fef ] In the error path of rtrs_srv_create_path_files(), the sysfs root folders may already have been created and srv_path->kobj may already have been initialized. If a later step fails, the cleanup currently calls kobject_put(&srv_path->kobj) before rtrs_srv_destroy_once_sysfs_root_folders(srv_path). kobject_put() may drop the last reference to srv_path->kobj and invoke the release callback, rtrs_srv_release(), which frees srv_path. The following call to rtrs_srv_destroy_once_sysfs_root_folders(srv_path) then dereferences srv_path internally to access srv_path->srv, resulting in a use-after-free. This failure path is reached before rtrs_srv_create_path_files() returns success, so the successful-path lifetime handling is not involved. Fix this by destroying the sysfs root folders before calling kobject_put(&srv_path->kobj), so srv_path is still valid while the helper accesses it. This issue was found by a static analysis tool I am developing. Fixes: ae4c81644e91 ("RDMA/rtrs-srv: Rename rtrs_srv_sess to rtrs_srv_path") Signed-off-by: Guangshuo Li Link: https://patch.msgid.link/20260514113834.865530-1-lgs201920130244@gmail.com Signed-off-by: Leon Romanovsky Signed-off-by: Sasha Levin --- drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c index 3f305e694fe8..1b1c6ea4ee5a 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c @@ -295,8 +295,8 @@ int rtrs_srv_create_path_files(struct rtrs_srv_path *srv_path) put_kobj: kobject_del(&srv_path->kobj); destroy_root: - kobject_put(&srv_path->kobj); rtrs_srv_destroy_once_sysfs_root_folders(srv_path); + kobject_put(&srv_path->kobj); return err; } From 981aea2099770509aa41969c179afe52f87d20da Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 23 Oct 2025 16:45:37 +0200 Subject: [PATCH 337/697] net: bridge: Flush multicast groups when snooping is disabled [ Upstream commit 68800bbf583f26f71491141e4b3c8582f9cfcbde ] When forwarding multicast packets, the bridge takes MDB into account when IGMP / MLD snooping is enabled. Currently, when snooping is disabled, the MDB is retained, even though it is not used anymore. At the same time, during the time that snooping is disabled, the IGMP / MLD control packets are obviously ignored, and after the snooping is reenabled, the administrator has to assume it is out of sync. In particular, missed join and leave messages would lead to traffic being forwarded to wrong interfaces. Keeping the MDB entries around thus serves no purpose, and just takes memory. Note also that disabling per-VLAN snooping does actually flush the relevant MDB entries. This patch flushes non-permanent MDB entries as global snooping is disabled. Signed-off-by: Petr Machata Reviewed-by: Ido Schimmel Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/5e992df1bb93b88e19c0ea5819e23b669e3dde5d.1761228273.git.petrm@nvidia.com Signed-off-by: Jakub Kicinski Stable-dep-of: 4df78ff02629 ("bridge: mcast: Fix a possible use-after-free when removing a bridge port") Signed-off-by: Sasha Levin --- net/bridge/br_multicast.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 5855eb050208..e9a7e6530401 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -4640,6 +4640,14 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, rcu_read_unlock(); } +static void br_multicast_del_grps(struct net_bridge *br) +{ + struct net_bridge_port *port; + + list_for_each_entry(port, &br->port_list, list) + __br_multicast_disable_port_ctx(&port->multicast_ctx); +} + int br_multicast_toggle(struct net_bridge *br, unsigned long val, struct netlink_ext_ack *extack) { @@ -4660,6 +4668,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { change_snoopers = true; + br_multicast_del_grps(br); goto unlock; } From a9224862d597d0eed0a34bbb27343f703fc4113f Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 17 May 2026 15:11:21 +0300 Subject: [PATCH 338/697] bridge: mcast: Fix a possible use-after-free when removing a bridge port [ Upstream commit 4df78ff02629c7729168f0696a7a2123c389818d ] When per-VLAN multicast snooping is enabled, the bridge iterates over all the bridge ports, disables the per-port multicast context on each port and enables the per-{port, VLAN} multicast contexts instead. The reverse happens when per-VLAN multicast snooping is disabled. When global multicast snooping is enabled, the bridge iterates over all the bridge ports and enables the per-port multicast context on each port. The reverse happens when multicast snooping is disabled. The above scheme can result in a situation where both types of contexts (per-port and per-{port, VLAN}) are enabled on a single bridge port: # ip link add name br1 up type bridge mcast_snooping 1 mcast_querier 1 vlan_filtering 1 # ip link add name dummy1 up master br1 type dummy # ip link set dev br1 type bridge mcast_vlan_snooping 1 # ip link set dev br1 type bridge mcast_snooping 0 # ip link set dev br1 type bridge mcast_snooping 1 This is not intended and it is a problem since the commit cited below. Prior to this commit, when removing a bridge port, br_multicast_disable_port() would disable the per-port multicast context and the per-{port, VLAN} multicast contexts would get disabled when flushing VLANs. After this commit, br_multicast_disable_port() only disables the per-port multicast context if per-VLAN multicast snooping is disabled. If both types of contexts were enabled on the port when it was removed, the per-port multicast context would remain enabled when freeing the bridge port, leading to a use-after-free [1]. Fix by preventing the bridge from enabling / disabling the per-port multicast contexts when toggling global multicast snooping if per-VLAN multicast snooping is enabled. [1] ODEBUG: free active (active state 0) object: ffff88810f8bda78 object type: timer_list hint: br_ip6_multicast_port_query_expired (net/bridge/br_multicast.c:1927) WARNING: lib/debugobjects.c:629 at debug_print_object+0x1b1/0x3e0, CPU#5: swapper/5/0 [...] Call Trace: __debug_check_no_obj_freed (lib/debugobjects.c:1116) kfree (mm/slub.c:2620 mm/slub.c:6250 mm/slub.c:6565) kobject_cleanup (lib/kobject.c:689) rcu_do_batch (kernel/rcu/tree.c:2617) rcu_core (kernel/rcu/tree.c:2869) handle_softirqs (kernel/softirq.c:622) __irq_exit_rcu (kernel/softirq.c:656 kernel/softirq.c:496 kernel/softirq.c:735) irq_exit_rcu (kernel/softirq.c:752) sysvec_apic_timer_interrupt (arch/x86/kernel/apic/apic.c:1061 (discriminator 47) arch/x86/kernel/apic/apic.c:1061 (discriminator 47)) Fixes: 4b30ae9adb04 ("net: bridge: mcast: re-implement br_multicast_{enable, disable}_port functions") Reported-by: syzbot+ae231e0552fa77b26ea1@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/87qznowlfs.ffs@tglx/ Reported-by: Thomas Gleixner Acked-by: Nikolay Aleksandrov Signed-off-by: Ido Schimmel Link: https://patch.msgid.link/20260517121122.188333-2-idosch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_multicast.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index e9a7e6530401..b77b3250afbd 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -4640,10 +4640,24 @@ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx, rcu_read_unlock(); } -static void br_multicast_del_grps(struct net_bridge *br) +static void br_multicast_enable_all_ports(struct net_bridge *br) { struct net_bridge_port *port; + if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) + return; + + list_for_each_entry(port, &br->port_list, list) + __br_multicast_enable_port_ctx(&port->multicast_ctx); +} + +static void br_multicast_disable_all_ports(struct net_bridge *br) +{ + struct net_bridge_port *port; + + if (br_opt_get(br, BROPT_MCAST_VLAN_SNOOPING_ENABLED)) + return; + list_for_each_entry(port, &br->port_list, list) __br_multicast_disable_port_ctx(&port->multicast_ctx); } @@ -4651,7 +4665,6 @@ static void br_multicast_del_grps(struct net_bridge *br) int br_multicast_toggle(struct net_bridge *br, unsigned long val, struct netlink_ext_ack *extack) { - struct net_bridge_port *port; bool change_snoopers = false; int err = 0; @@ -4668,7 +4681,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, br_opt_toggle(br, BROPT_MULTICAST_ENABLED, !!val); if (!br_opt_get(br, BROPT_MULTICAST_ENABLED)) { change_snoopers = true; - br_multicast_del_grps(br); + br_multicast_disable_all_ports(br); goto unlock; } @@ -4676,8 +4689,7 @@ int br_multicast_toggle(struct net_bridge *br, unsigned long val, goto unlock; br_multicast_open(br); - list_for_each_entry(port, &br->port_list, list) - __br_multicast_enable_port_ctx(&port->multicast_ctx); + br_multicast_enable_all_ports(br); change_snoopers = true; From bd731994cff13aba2a1786f5b5c2c042f6d903d1 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 18 May 2026 10:23:09 +0200 Subject: [PATCH 339/697] net: phy: honor eee_disabled_modes in phy_support_eee() [ Upstream commit 3655063e083889ed4b79b7dda9cec65478dce09a ] phy_support_eee() copies supported_eee into advertising_eee unconditionally, overwriting any filtering applied during phy_probe() based on DT eee-broken-* properties or driver-populated eee_disabled_modes. MAC drivers that call phy_support_eee() after probe (e.g. bcmgenet, fec, lan743x, lan78xx, r8169) then cause the PHY to advertise EEE for modes the user marked as broken. The symptom is that ethtool --show-eee on the local interface reports "not supported" (supported & ~eee_disabled_modes is empty) while the link partner sees EEE negotiated and active. phy_probe() already filters advertising_eee via eee_disabled_modes after calling of_set_phy_eee_broken(). Apply the same mask in phy_support_eee() so the filtering survives the copy. Fixes: 49168d1980e2 ("net: phy: Add phy_support_eee() indicating MAC support EEE") Signed-off-by: Nicolai Buchwitz Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-1-05b52626fa68@tipi-net.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/phy_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index dea8b94286d1..5d76bd1ffe27 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2818,7 +2818,8 @@ EXPORT_SYMBOL_GPL(phy_advertise_eee_all); */ void phy_support_eee(struct phy_device *phydev) { - linkmode_copy(phydev->advertising_eee, phydev->supported_eee); + linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, + phydev->eee_disabled_modes); phydev->eee_cfg.tx_lpi_enabled = true; phydev->eee_cfg.eee_enabled = true; } From 0c277d203684c27bff62d6bd997a9fd77c88884f Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Mon, 18 May 2026 10:23:10 +0200 Subject: [PATCH 340/697] net: phy: honor eee_disabled_modes in phy_advertise_eee_all() [ Upstream commit 8baa7506d793f0636e3f6f01b01ef7be19674d06 ] phy_advertise_eee_all() copies supported_eee into advertising_eee unconditionally, overwriting any filtering applied during phy_probe() based on DT eee-broken-* properties or driver-populated eee_disabled_modes. genphy_c45_ethtool_set_eee() calls this helper when user space passes an empty advertisement, undoing the filtering. Apply the same eee_disabled_modes mask in phy_advertise_eee_all() so the filtering survives the copy, matching the pattern in phy_probe() and phy_support_eee(). Fixes: b64691274f5d ("net: phy: add helper phy_advertise_eee_all") Signed-off-by: Nicolai Buchwitz Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260518-devel-phy-support-eee-fix-v2-2-05b52626fa68@tipi-net.de Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/phy_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 5d76bd1ffe27..78cf05a17f8f 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2792,7 +2792,8 @@ EXPORT_SYMBOL(phy_advertise_supported); */ void phy_advertise_eee_all(struct phy_device *phydev) { - linkmode_copy(phydev->advertising_eee, phydev->supported_eee); + linkmode_andnot(phydev->advertising_eee, phydev->supported_eee, + phydev->eee_disabled_modes); } EXPORT_SYMBOL_GPL(phy_advertise_eee_all); From ce23832071aff284dc928775c0f8042c50b35cba Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Mon, 18 May 2026 15:44:57 +0200 Subject: [PATCH 341/697] net: airoha: Fix NPU RX DMA descriptor bits [ Upstream commit 0cb5a74faa3bdcfa3b18735d554e12c0f615e35d ] In an internal review from Airoha, it was notice that the RX DMA descriptor bits and mask are wrong. These values probably refer to an old NPU firmware never published. The previous value works correctly but it was reported that in some specific condition in mixed scenario with both Ethernet and WiFi offload it's possible that RX DMA descriptor signal wrong value with the problem to the RX ring or packets getting dropped. To handle these specific scenario, apply the new suggested bits mask from Airoha. Correct functionality of both AN7581 NPU and MT7996 variant were verified and confirmed working. Fixes: a7fc8c641cab ("net: airoha: Fix npu rx DMA definitions") Signed-off-by: Christian Marangi Acked-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260518134530.3683-1-ansuelsmth@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/linux/soc/airoha/airoha_offload.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/soc/airoha/airoha_offload.h b/include/linux/soc/airoha/airoha_offload.h index 0e82f1f4d36c..d4f6e8124a49 100644 --- a/include/linux/soc/airoha/airoha_offload.h +++ b/include/linux/soc/airoha/airoha_offload.h @@ -70,9 +70,9 @@ static inline void airoha_ppe_dev_check_skb(struct airoha_ppe_dev *dev, #define NPU_RX1_DESC_NUM 512 /* CTRL */ -#define NPU_RX_DMA_DESC_LAST_MASK BIT(27) -#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(26, 14) -#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(13, 1) +#define NPU_RX_DMA_DESC_LAST_MASK BIT(29) +#define NPU_RX_DMA_DESC_LEN_MASK GENMASK(28, 15) +#define NPU_RX_DMA_DESC_CUR_LEN_MASK GENMASK(14, 1) #define NPU_RX_DMA_DESC_DONE_MASK BIT(0) /* INFO */ #define NPU_RX_DMA_PKT_COUNT_MASK GENMASK(31, 29) From 784dd2bdc622ed3cc6ef8e113aa1852e252de36f Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Fri, 15 May 2026 21:29:05 +0000 Subject: [PATCH 342/697] pds_core: fix error handling in pdsc_devcmd_wait [ Upstream commit 0e46b6635b03d29807f810c3b415c4755a3f958d ] Fix two cases where pdsc_devcmd_wait() returns stale success from the completion register instead of an error: 1. FW crash: If firmware stops running, the wait loop breaks early with running=false. The condition "if ((!done || timeout) && running)" is false, so error handling is bypassed and stale status is returned. Check !running first and return -ENXIO. 2. Timeout: If a command times out, err is set to -ETIMEDOUT but then overwritten by pdsc_err_to_errno(status) which reads stale status. Return -ETIMEDOUT immediately after cleaning up. Both errors now propagate to pdsc_devcmd_locked() which queues health_work for recovery. Fixes: 45d76f492938 ("pds_core: set up device and adminq") Signed-off-by: Nikhil P. Rao Link: https://patch.msgid.link/20260515212907.998028-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/dev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/dev.c b/drivers/net/ethernet/amd/pds_core/dev.c index 495ef4ef8c10..1d1e559bd99d 100644 --- a/drivers/net/ethernet/amd/pds_core/dev.c +++ b/drivers/net/ethernet/amd/pds_core/dev.c @@ -162,12 +162,19 @@ static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds) dev_dbg(dev, "DEVCMD %d %s after %ld secs\n", opcode, pdsc_devcmd_str(opcode), duration / HZ); - if ((!done || timeout) && running) { + if (!running) { + dev_err(dev, "DEVCMD %d %s fw not running\n", + opcode, pdsc_devcmd_str(opcode)); + pdsc_devcmd_clean(pdsc); + return -ENXIO; + } + + if (!done || timeout) { dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n", opcode, pdsc_devcmd_str(opcode), done, timeout, max_seconds); - err = -ETIMEDOUT; pdsc_devcmd_clean(pdsc); + return -ETIMEDOUT; } status = pdsc_devcmd_status(pdsc); From 91d13e92b983e6c6d7631012c2e20ae8057de9f2 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Fri, 15 May 2026 21:29:07 +0000 Subject: [PATCH 343/697] pds_core: fix debugfs_lookup dentry leak and error handling [ Upstream commit dc416e32baaeb620b9809e9e25fc7b30889686e9 ] debugfs_lookup() returns a dentry with an elevated reference count that must be released with dput(). The current code discards the returned dentry without calling dput(), causing a reference leak on every firmware reset recovery. Additionally, when CONFIG_DEBUG_FS is disabled, debugfs_lookup() returns ERR_PTR(-ENODEV), not NULL. The current check passes for error pointers and would call dput() on an invalid pointer, causing a crash. Fixes: bc90fbe0c318 ("pds_core: Rework teardown/setup flow to be more common") Signed-off-by: Nikhil P. Rao Link: https://patch.msgid.link/20260515212907.998028-3-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/debugfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/amd/pds_core/debugfs.c b/drivers/net/ethernet/amd/pds_core/debugfs.c index 04c5e3abd8d7..810a0cd9bcac 100644 --- a/drivers/net/ethernet/amd/pds_core/debugfs.c +++ b/drivers/net/ethernet/amd/pds_core/debugfs.c @@ -64,9 +64,14 @@ DEFINE_SHOW_ATTRIBUTE(identity); void pdsc_debugfs_add_ident(struct pdsc *pdsc) { + struct dentry *dentry; + /* This file will already exist in the reset flow */ - if (debugfs_lookup("identity", pdsc->dentry)) + dentry = debugfs_lookup("identity", pdsc->dentry); + if (!IS_ERR_OR_NULL(dentry)) { + dput(dentry); return; + } debugfs_create_file("identity", 0400, pdsc->dentry, pdsc, &identity_fops); From 425d32d6288d7d845e486af9419bbedccd8c9103 Mon Sep 17 00:00:00 2001 From: Gao Xiang Date: Tue, 28 Apr 2026 12:34:31 +0800 Subject: [PATCH 344/697] erofs: fix managed cache race for unaligned extents [ Upstream commit 649932fc3815eda2f24eb4de4b3a5e94886ee0b9 ] After unaligned compressed extents were introduced, the following race could occur: [Thread 1] [Thread 2] (z_erofs_fill_bio_vec) ... filemap_add_folio (1) (z_erofs_bind_cache) .. .. folio_attach_private (2) filemap_add_folio (3) again Since (1) is executed but (2) hasn't been executed yet, it's possible that another thread finds the same managed folio in z_erofs_bind_cache() for a different pcluster and calls filemap_add_folio() again since folio->private is still Z_EROFS_PREALLOCATED_FOLIO. Fix this by explicitly clearing folio->private before making the folio visible in the managed cache so that another pcluster can simply wait on the locked managed folio as what we did for other shared cases [1]. This only impacts unaligned data compression (`-E48bit` with zstd, for example). [1] Commit 9e2f9d34dd12 ("erofs: handle overlapped pclusters out of crafted images properly") was originally introduced to handle crafted overlapped extents, but it addresses unaligned extents as well. Fixes: 7361d1e3763b ("erofs: support unaligned encoded data") Reported-by: Arseniy Krasnov Closes: https://lore.kernel.org/r/4a2f3801-fac1-42fe-ae75-da315822e088@salutedevices.com Tested-by: Arseniy Krasnov Signed-off-by: Gao Xiang Signed-off-by: Sasha Levin --- fs/erofs/zdata.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c index 71f01f0a0743..33932d56d3a4 100644 --- a/fs/erofs/zdata.c +++ b/fs/erofs/zdata.c @@ -1511,8 +1511,15 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, DBG_BUGON(z_erofs_is_shortlived_page(bvec->bv_page)); folio = page_folio(zbv.page); - /* For preallocated managed folios, add them to page cache here */ + /* + * Preallocated folios are added to the managed cache here rather than + * in z_erofs_bind_cache() in order to keep these folios locked in + * increasing (physical) address order. + * Clear folio->private before these folios become visible to others in + * the managed cache to avoid duplicate additions for unaligned extents. + */ if (folio->private == Z_EROFS_PREALLOCATED_FOLIO) { + folio->private = NULL; tocache = true; goto out_tocache; } @@ -1548,14 +1555,8 @@ static void z_erofs_fill_bio_vec(struct bio_vec *bvec, } return; } - /* - * Already linked with another pcluster, which only appears in - * crafted images by fuzzers for now. But handle this anyway. - */ - tocache = false; /* use temporary short-lived pages */ } else { DBG_BUGON(1); /* referenced managed folios can't be truncated */ - tocache = true; } folio_unlock(folio); folio_put(folio); From 2d8379834800c30602f24c71ab7c40f5fe84d200 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Fri, 15 May 2026 12:29:08 +0200 Subject: [PATCH 345/697] wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs [ Upstream commit f718506edd2d9c6a308ded9d13c632bf7b7d5a2c ] IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID is 0x000f, so link_id extracted from a PRIO_ACCESS ML element PER_STA_PROFILE subelement can be 0..15. sdata->link[] has IEEE80211_MLD_MAX_NUM_LINKS (15) entries (indices 0..14), making index 15 out-of-bounds. A connected WiFi 7 AP can trigger this by sending an EPCS Enable Response action frame with a PER_STA_PROFILE subelement where link_id = 15. The unsolicited-notification path (dialog_token = 0) is reachable any time EPCS is already enabled, without any prior client request. sdata->link[15] reads into the first word of sdata->activate_links_work (a wiphy_work whose embedded list_head is non-NULL after INIT_LIST_HEAD), so the NULL check on the result does not catch the invalid access. The garbage pointer is then passed to ieee80211_sta_wmm_params(), which dereferences link->sdata and crashes the kernel. The same class of bug was fixed for ieee80211_ml_reconfiguration() by commit 162d331d833d ("wifi: mac80211: bounds-check link_id in ieee80211_ml_reconfiguration"). Fixes: de86c5f60839 ("wifi: mac80211: Add support for EPCS configuration") Signed-off-by: Alexandru Hossu Link: https://patch.msgid.link/20260515102908.1653088-1-hossu.alexandru@gmail.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/mlme.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index ce247dde048c..5d1da779cd6f 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -10997,6 +10997,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata, control = get_unaligned_le16(pos); link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID; + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + continue; + link = sdata_dereference(sdata->link[link_id], sdata); if (!link) continue; From 55c479aae99b120489a432db9c717484e523dfd6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 8 May 2026 09:10:31 +0200 Subject: [PATCH 346/697] wifi: mac80211: fix MLE defragmentation [ Upstream commit a74e893f30db64cdce0fc7a96d3baa417bcd55f5 ] If either reconf or EPCS multi-link element (MLE) is contained in a non-transmitted profile, the defragmentation routine is called with a pointer to the defragmented copy, but the original elements. This is incorrect for two reasons: - if the original defragmentation was needed, it will not find the correct data - if the original frame is at a higher address, the parsing will potentially overrun the heap data (though given the layout of the buffers, only into the new defragmentation buffer, and then it has to stop and fail once that's filled with copied data. Fix it by tracking the container along with the pointer and in doing so also unify the two almost identical defragmentation routines. Fixes: 4d70e9c5488d ("wifi: mac80211: defragment reconfiguration MLE when parsing") Reviewed-by: Miriam Rachel Korenblit Reviewed-by: Ilan Peer Link: https://patch.msgid.link/20260508091031.8a6c34613178.I4de16ebbce2d27f2f8f98fc49949c7a376c2fe8d@changeid Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- net/mac80211/parse.c | 71 +++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c index c5e0f7f46004..b9ec99f51851 100644 --- a/net/mac80211/parse.c +++ b/net/mac80211/parse.c @@ -34,6 +34,13 @@ #include "led.h" #include "wep.h" +struct ieee80211_elem_defrag { + const struct element *elem; + /* container start/len */ + const u8 *start; + size_t len; +}; + struct ieee80211_elems_parse { /* must be first for kfree to work */ struct ieee802_11_elems elems; @@ -41,11 +48,7 @@ struct ieee80211_elems_parse { /* The basic Multi-Link element in the original elements */ const struct element *ml_basic_elem; - /* The reconfiguration Multi-Link element in the original elements */ - const struct element *ml_reconf_elem; - - /* The EPCS Multi-Link element in the original elements */ - const struct element *ml_epcs_elem; + struct ieee80211_elem_defrag ml_reconf, ml_epcs; bool multi_link_inner; bool skip_vendor; @@ -162,10 +165,14 @@ ieee80211_parse_extension_element(u32 *crc, } break; case IEEE80211_ML_CONTROL_TYPE_RECONF: - elems_parse->ml_reconf_elem = elem; + elems_parse->ml_reconf.elem = elem; + elems_parse->ml_reconf.start = params->start; + elems_parse->ml_reconf.len = params->len; break; case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS: - elems_parse->ml_epcs_elem = elem; + elems_parse->ml_epcs.elem = elem; + elems_parse->ml_epcs.start = params->start; + elems_parse->ml_epcs.len = params->len; break; default: break; @@ -950,46 +957,27 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse, sub->start, sub->len); } -static void -ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse) -{ - struct ieee802_11_elems *elems = &elems_parse->elems; - ssize_t ml_len; - - ml_len = cfg80211_defragment_element(elems_parse->ml_reconf_elem, - elems->ie_start, - elems->total_len, - elems_parse->scratch_pos, - elems_parse->scratch + - elems_parse->scratch_len - - elems_parse->scratch_pos, - WLAN_EID_FRAGMENT); - if (ml_len < 0) - return; - elems->ml_reconf = (void *)elems_parse->scratch_pos; - elems->ml_reconf_len = ml_len; - elems_parse->scratch_pos += ml_len; -} - -static void -ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse) +static const void * +ieee80211_mle_defrag(struct ieee80211_elems_parse *elems_parse, + struct ieee80211_elem_defrag *defrag, + size_t *out_len) { - struct ieee802_11_elems *elems = &elems_parse->elems; + const void *ret; ssize_t ml_len; - ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem, - elems->ie_start, - elems->total_len, + ml_len = cfg80211_defragment_element(defrag->elem, + defrag->start, defrag->len, elems_parse->scratch_pos, elems_parse->scratch + elems_parse->scratch_len - elems_parse->scratch_pos, WLAN_EID_FRAGMENT); if (ml_len < 0) - return; - elems->ml_epcs = (void *)elems_parse->scratch_pos; - elems->ml_epcs_len = ml_len; + return NULL; + ret = elems_parse->scratch_pos; + *out_len = ml_len; elems_parse->scratch_pos += ml_len; + return ret; } struct ieee802_11_elems * @@ -1069,9 +1057,12 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) _ieee802_11_parse_elems_full(&sub, elems_parse, NULL); } - ieee80211_mle_defrag_reconf(elems_parse); - - ieee80211_mle_defrag_epcs(elems_parse); + elems->ml_reconf = ieee80211_mle_defrag(elems_parse, + &elems_parse->ml_reconf, + &elems->ml_reconf_len); + elems->ml_epcs = ieee80211_mle_defrag(elems_parse, + &elems_parse->ml_epcs, + &elems->ml_epcs_len); if (elems->tim && !elems->parse_error) { const struct ieee80211_tim_ie *tim_ie = elems->tim; From 95c82d498d74c4e587db30021ca1aec90e29b5a5 Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Mon, 11 May 2026 09:57:32 +0530 Subject: [PATCH 347/697] wifi: wilc1000: fix dma_buffer leak on bus acquire failure [ Upstream commit dd7b6a8671939708cc4b7a46786d8c11297e8f69 ] wilc_wlan_firmware_download() allocates dma_buffer with kmalloc() at the top of the function and uses a 'fail:' label to free it via kfree(dma_buffer) on error. All later error paths correctly use 'goto fail' to route through this cleanup. However, the early failure path after the first acquire_bus() call uses a bare 'return ret;', which leaks dma_buffer whenever the bus acquire fails. Replace the early return with goto fail so the existing cleanup path runs. Found via a custom Coccinelle semantic patch hunting for kmalloc'd locals leaked on early-return error paths in driver firmware-download code. Fixes: 1241c5650ff7 ("wifi: wilc1000: Fill in missing error handling") Signed-off-by: Shitalkumar Gandhi Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260511042732.998311-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Johannes Berg Signed-off-by: Sasha Levin --- drivers/net/wireless/microchip/wilc1000/wlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/microchip/wilc1000/wlan.c b/drivers/net/wireless/microchip/wilc1000/wlan.c index fedc7d59216a..1cc4ee62987d 100644 --- a/drivers/net/wireless/microchip/wilc1000/wlan.c +++ b/drivers/net/wireless/microchip/wilc1000/wlan.c @@ -1265,7 +1265,7 @@ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer, ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP); if (ret) - return ret; + goto fail; wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, ®); reg &= ~BIT(10); From 3aab4a58d23fb22dac5b558bbe5df1a8dad00b4b Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Wed, 20 May 2026 18:32:49 +0800 Subject: [PATCH 348/697] ALSA: seq: Serialize UMP output teardown with event_input [ Upstream commit 60a1969fae6209644698fca91c185d153674f631 ] seq_ump_process_event() borrows client->out_rfile.output without synchronizing with the first-open and last-close transition in seq_ump_client_open() and seq_ump_client_close(). The last output unuse can therefore drop opened[STR_OUT] to zero and release the rawmidi file while an in-flight event_input callback is still inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream runtime exposed to teardown before the write path has taken its own buffer reference. Add a per-client rwlock for the event_input-visible output file. Publish a newly opened output file under the write side, and hold the read side from the output lookup through snd_rawmidi_kernel_write(). The last output close copies and clears the visible output file under the write side, then drops the lock and releases the saved rawmidi file. Use IRQ-safe rwlock guards because event_input can also be reached from atomic sequencer delivery. The buggy scenario involves two paths, with each column showing the order within that path: path A label: event_input path path B label: last unuse path 1. seq_ump_process_event() reads 1. seq_ump_client_close() client->out_rfile.output. drops opened[STR_OUT] to zero. 2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release() has not yet pinned runtime. closes the output file. 3. The writer continues using 3. close_substream() frees the borrowed substream. substream->runtime. This keeps the output substream and runtime alive for the full event_input write while keeping rawmidi release outside the rwlock. KASAN reproduced this as a slab-use-after-free in snd_rawmidi_kernel_write1(), with allocation through seq_ump_use()/snd_seq_port_connect() and free through seq_ump_unuse()/snd_seq_port_disconnect(). Suggested-by: Takashi Iwai Validation reproduced this kernel report: KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400 RIP: 0033:0x7f5528af837f Read of size 8 Call trace: dump_stack_lvl+0x73/0xb0 (?:?) print_report+0xd1/0x650 (?:?) srso_alias_return_thunk+0x5/0xfbef5 (?:?) __virt_addr_valid+0x1a7/0x340 (?:?) kasan_complete_mode_report_info+0x64/0x200 (?:?) kasan_report+0xf7/0x130 (?:?) snd_rawmidi_kernel_write1+0x9d/0x400 (?:?) __asan_load8+0x82/0xb0 (?:?) update_stack_state+0x1ef/0x2d0 (?:?) snd_rawmidi_kernel_write+0x1a/0x20 (?:?) seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82) __snd_seq_deliver_single_event+0x8a/0xe0 (?:?) snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?) lock_acquire+0x14e/0x2e0 (?:?) find_held_lock+0x31/0x90 (?:?) snd_seq_port_use_ptr+0xa6/0xe0 (?:?) __kasan_check_write+0x18/0x20 (?:?) do_raw_read_unlock+0x32/0xa0 (?:?) _raw_read_unlock+0x26/0x50 (?:?) snd_seq_deliver_single_event+0x45c/0x4b0 (?:?) snd_seq_deliver_event+0x10d/0x1b0 (?:?) snd_seq_client_enqueue_event+0x192/0x240 (?:?) snd_seq_write+0x2cd/0x450 (?:?) apparmor_file_permission+0x20/0x30 (?:?) security_file_permission+0x51/0x60 (?:?) vfs_write+0x1ce/0x850 (?:?) __fget_files+0x12b/0x220 (?:?) lock_release+0xc8/0x2a0 (?:?) __rcu_read_unlock+0x74/0x2d0 (?:?) __fget_files+0x135/0x220 (?:?) ksys_write+0x15a/0x180 (?:?) rcu_is_watching+0x24/0x60 (?:?) __x64_sys_write+0x46/0x60 (?:?) x64_sys_call+0x7d/0x20d0 (?:?) do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87) entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?) Fixes: 81fd444aa371 ("ALSA: seq: Bind UMP device") Signed-off-by: Zhang Cen Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/seq/seq_ump_client.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/sound/core/seq/seq_ump_client.c b/sound/core/seq/seq_ump_client.c index ebbe1cbe0b9b..9a6b4b61bd74 100644 --- a/sound/core/seq/seq_ump_client.c +++ b/sound/core/seq/seq_ump_client.c @@ -37,6 +37,7 @@ struct seq_ump_client { struct snd_ump_endpoint *ump; /* assigned endpoint */ int seq_client; /* sequencer client id */ int opened[2]; /* current opens for each direction */ + rwlock_t output_lock; /* protects out_rfile output access */ struct snd_rawmidi_file out_rfile; /* rawmidi for output */ struct seq_ump_input_buffer input; /* input parser context */ void *ump_info[SNDRV_UMP_MAX_BLOCKS + 1]; /* shadow of seq client ump_info */ @@ -88,6 +89,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, unsigned char type; int len; + guard(read_lock_irqsave)(&client->output_lock); substream = client->out_rfile.output; if (!substream) return -ENODEV; @@ -106,6 +108,7 @@ static int seq_ump_process_event(struct snd_seq_event *ev, int direct, static int seq_ump_client_open(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; + struct snd_rawmidi_file rfile = {}; int err; guard(mutex)(&ump->open_mutex); @@ -113,9 +116,11 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) err = snd_rawmidi_kernel_open(&ump->core, 0, SNDRV_RAWMIDI_LFLG_OUTPUT | SNDRV_RAWMIDI_LFLG_APPEND, - &client->out_rfile); + &rfile); if (err < 0) return err; + scoped_guard(write_lock_irqsave, &client->output_lock) + client->out_rfile = rfile; } client->opened[dir]++; return 0; @@ -125,11 +130,19 @@ static int seq_ump_client_open(struct seq_ump_client *client, int dir) static int seq_ump_client_close(struct seq_ump_client *client, int dir) { struct snd_ump_endpoint *ump = client->ump; + struct snd_rawmidi_file rfile = {}; guard(mutex)(&ump->open_mutex); - if (!--client->opened[dir]) - if (dir == STR_OUT) - snd_rawmidi_kernel_release(&client->out_rfile); + if (!--client->opened[dir]) { + if (dir == STR_OUT) { + scoped_guard(write_lock_irqsave, &client->output_lock) { + rfile = client->out_rfile; + client->out_rfile = (struct snd_rawmidi_file){}; + } + if (rfile.rmidi) + snd_rawmidi_kernel_release(&rfile); + } + } return 0; } @@ -468,6 +481,7 @@ static int snd_seq_ump_probe(struct device *_dev) INIT_WORK(&client->group_notify_work, handle_group_notify); client->ump = ump; + rwlock_init(&client->output_lock); client->seq_client = snd_seq_create_kernel_client(card, ump->core.device, From 8bf00d3ac425ac0df1c0b2a82e32cdd9789964c3 Mon Sep 17 00:00:00 2001 From: Cunlong Li Date: Wed, 20 May 2026 11:30:54 +0800 Subject: [PATCH 349/697] cgroup: rstat: relax NMI guard after switch to try_cmpxchg [ Upstream commit 22572dbcd3486e6c4dced877125bbf50e4e24edf ] Commit 36df6e3dbd7e ("cgroup: make css_rstat_updated nmi safe") used this_cpu_cmpxchg() for the lockless insertion, and therefore required both ARCH_HAVE_NMI_SAFE_CMPXCHG and ARCH_HAS_NMI_SAFE_THIS_CPU_OPS in the NMI guard: on archs without the latter, this_cpu_cmpxchg() falls back to "local_irq_save() + plain cmpxchg", and local_irq_save() cannot mask NMIs. Commit 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in css_rstat_updated") later replaced this_cpu_cmpxchg() with plain try_cmpxchg() to fix cross-CPU lockless-list corruption, but left the NMI guard untouched. After that switch, css_rstat_updated() no longer performs any this_cpu_*() RMW operations and only relies on the arch having NMI-safe cmpxchg, so ARCH_HAS_NMI_SAFE_THIS_CPU_OPS is no longer required in the guard. Relax the guard accordingly so that archs which have HAVE_NMI and ARCH_HAVE_NMI_SAFE_CMPXCHG but not ARCH_HAS_NMI_SAFE_THIS_CPU_OPS (e.g. sparc, powerpc on PPC64/BOOK3S) can benefit from the existing CONFIG_MEMCG_NMI_SAFETY_REQUIRES_ATOMIC path. Without this, the css is never queued in NMI on those archs, and the atomics staged by account_{slab,kmem}_nmi_safe() are not drained by flush_nmi_stats(). Fixes: 3309b63a2281 ("cgroup: rstat: use LOCK CMPXCHG in css_rstat_updated") Signed-off-by: Cunlong Li Signed-off-by: Tejun Heo Signed-off-by: Sasha Levin --- kernel/cgroup/rstat.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c index ed60ba119c68..de816a43db9f 100644 --- a/kernel/cgroup/rstat.c +++ b/kernel/cgroup/rstat.c @@ -81,11 +81,10 @@ void __css_rstat_updated(struct cgroup_subsys_state *css, int cpu) lockdep_assert_preemption_disabled(); /* - * For archs withnot nmi safe cmpxchg or percpu ops support, ignore - * the requests from nmi context. + * The lockless insertion below relies on NMI-safe cmpxchg; + * bail out in NMI on archs that don't provide it. */ - if ((!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) || - !IS_ENABLED(CONFIG_ARCH_HAS_NMI_SAFE_THIS_CPU_OPS)) && in_nmi()) + if (!IS_ENABLED(CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG) && in_nmi()) return; rstatc = css_rstat_cpu(css, cpu); From d6c8b3ebdcdb12b59ad4212acb137cc56cae453d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 8 May 2026 20:57:47 +0100 Subject: [PATCH 350/697] tracing: Avoid NULL return from hist_field_name() on truncation [ Upstream commit 576ec047d20b368b43c4d5db98c4f2e0f3c101ec ] hist_field_name() returns "" everywhere except the fully-qualified VAR_REF/EXPR case, where snprintf() truncation returns NULL early and bypasses the bottom NULL->"" guard. Callers don't expect NULL: strcat(expr, hist_field_name(field, 0)) at trace_events_hist.c:1758 and the strcmp() in the sort-key match loop at :4804 both deref it. system and event_name are bounded by MAX_EVENT_NAME_LEN, but the field name on a VAR_REF is kstrdup'd from a histogram variable name parsed out of the trigger string and has no length cap, so a long enough var name in a fully qualified reference can reach the truncation path. Keep the length check but leave field_name as "" on overflow. Link: https://patch.msgid.link/20260508195747.25492-1-devnexen@gmail.com Fixes: 5ec1d1e97de1 ("tracing: Rebuild full_name on each hist_field_name() call") Signed-off-by: David Carlier Signed-off-by: Steven Rostedt Signed-off-by: Sasha Levin --- kernel/trace/trace_events_hist.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c index cb9e06713868..0089c257b465 100644 --- a/kernel/trace/trace_events_hist.c +++ b/kernel/trace/trace_events_hist.c @@ -1360,10 +1360,8 @@ static const char *hist_field_name(struct hist_field *field, len = snprintf(full_name, sizeof(full_name), "%s.%s.%s", field->system, field->event_name, field->name); - if (len >= sizeof(full_name)) - return NULL; - - field_name = full_name; + if (len < sizeof(full_name)) + field_name = full_name; } else field_name = field->name; } else if (field->flags & HIST_FIELD_FL_TIMESTAMP) From c7860b6a6d2d69d18566f10dfa8751c874b23ef1 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Fri, 15 May 2026 00:32:48 +0530 Subject: [PATCH 351/697] Bluetooth: btintel_pcie: Fix incorrect MAC access programming [ Upstream commit 88365d04fdc821dc4e9eb0cc00fdf6905430d172 ] btintel_pcie_get_mac_access() and btintel_pcie_release_mac_access() were programming STOP_MAC_ACCESS_DIS and XTAL_CLK_REQ in addition to the MAC_ACCESS_REQ handshake. These bits are not part of the host MAC-access handshake on the supported parts; the driver was programming them incorrectly. Drop the writes so the register update contains only the bits the controller actually consumes. Fixes: b9465e6670a2 ("Bluetooth: btintel_pcie: Read hardware exception data") Signed-off-by: Kiran K Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btintel_pcie.c | 20 ++++++-------------- drivers/bluetooth/btintel_pcie.h | 3 --- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c index c68a8de3025b..d0aa1666d6ac 100644 --- a/drivers/bluetooth/btintel_pcie.c +++ b/drivers/bluetooth/btintel_pcie.c @@ -567,12 +567,10 @@ static int btintel_pcie_get_mac_access(struct btintel_pcie_data *data) reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); - reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; - reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; - if ((reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS) == 0) + if (!(reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ)) { reg |= BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; - - btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); + } do { reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); @@ -592,16 +590,10 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data) reg = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG); - if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) + if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ) { reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ; - - if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS) - reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS; - - if (reg & BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ) - reg &= ~BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ; - - btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); + btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_FUNC_CTRL_REG, reg); + } } static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type, diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h index 48e1ae1793e5..481de85456e2 100644 --- a/drivers/bluetooth/btintel_pcie.h +++ b/drivers/bluetooth/btintel_pcie.h @@ -34,9 +34,6 @@ #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_STS (BIT(20)) #define BTINTEL_PCIE_CSR_FUNC_CTRL_MAC_ACCESS_REQ (BIT(21)) -/* Stop MAC Access disconnection request */ -#define BTINTEL_PCIE_CSR_FUNC_CTRL_STOP_MAC_ACCESS_DIS (BIT(22)) -#define BTINTEL_PCIE_CSR_FUNC_CTRL_XTAL_CLK_REQ (BIT(23)) #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_STS (BIT(28)) #define BTINTEL_PCIE_CSR_FUNC_CTRL_BUS_MASTER_DISCON (BIT(29)) From a0f5268c77eb73f84ba7c210ddfc54b1c73ff80c Mon Sep 17 00:00:00 2001 From: Jiajia Liu Date: Mon, 18 May 2026 10:24:02 +0800 Subject: [PATCH 352/697] Bluetooth: btmtk: fix urb->setup_packet leak in error paths [ Upstream commit dd1dda6b8d6e1f4376a5b3055a04f0ecbdb4d6bd ] The setup_packet of control urb is not freed if usb_submit_urb fails or the submitted urb is killed. Add free in these two paths. Fixes: a1c49c434e150 ("Bluetooth: btusb: Add protocol support for MediaTek MT7668U USB devices") Signed-off-by: Jiajia Liu Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- drivers/bluetooth/btmtk.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index cf05746e9db5..38ba8f2bd2f9 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -496,6 +496,7 @@ static void btmtk_usb_wmt_recv(struct urb *urb) return; } else if (urb->status == -ENOENT) { /* Avoid suspend failed when usb_kill_urb */ + kfree(urb->setup_packet); return; } @@ -569,6 +570,7 @@ static int btmtk_usb_submit_wmt_recv_urb(struct hci_dev *hdev) if (err != -EPERM && err != -ENODEV) bt_dev_err(hdev, "urb %p submission failed (%d)", urb, -err); + kfree(dr); usb_unanchor_urb(urb); } From b5bd4249e430f5963d559708ee96a671716d2400 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Sun, 10 May 2026 23:59:00 +0100 Subject: [PATCH 353/697] net/mlx5e: Fix eswitch mode block underflow on IPsec acquire SA [ Upstream commit abe003b33223ff33552f291644bf35d9c2f992fb ] mlx5e_xfrm_add_state() handles acquire-flow temporary SAs by allocating software state and skipping hardware offload setup. That path jumps to the common success label before taking the eswitch mode block. After tunnel-mode validation was moved earlier, the common success label unconditionally calls mlx5_eswitch_unblock_mode(). For acquire SAs, this decrements esw->offloads.num_block_mode without a matching increment. Return directly after installing the acquire SA offload handle, so only the paths that successfully called mlx5_eswitch_block_mode() call the matching unblock. Fixes: 22239eb258bc ("net/mlx5e: Prevent tunnel reformat when tunnel mode not allowed") Signed-off-by: Prathamesh Deshpande Reviewed-by: Simon Horman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260510225903.13184-1-prathameshdeshpande7@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c index f03507a522b4..51fb857a2766 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c @@ -794,8 +794,10 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, sa_entry->dev = dev; sa_entry->ipsec = ipsec; /* Check if this SA is originated from acquire flow temporary SA */ - if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) - goto out; + if (x->xso.flags & XFRM_DEV_OFFLOAD_FLAG_ACQ) { + x->xso.offload_handle = (unsigned long)sa_entry; + return 0; + } err = mlx5e_xfrm_validate_state(priv->mdev, x, extack); if (err) @@ -872,7 +874,6 @@ static int mlx5e_xfrm_add_state(struct net_device *dev, xa_unlock_bh(&ipsec->sadb); } -out: x->xso.offload_handle = (unsigned long)sa_entry; if (allow_tunnel_mode) mlx5_eswitch_unblock_encap(priv->mdev); From 5a2c2aa139c8da2dcda0bd24d415fa7471bf693b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 15 May 2026 15:13:24 -0700 Subject: [PATCH 354/697] net: shaper: annotate the data races [ Upstream commit a3442936dd0523277e20aaf86207c574e755c634 ] As previously discussed we don't care about making the shaper state fully RCU-compliant because the hierarchy itself can't be dumped in one go over Netlink. Let's annotate the reads and writes to make that clear. The field-by-field assignments will also be useful for the next commit which adds explicit "valid" field (which we don't want to override with the current full struct assignment). Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260515221325.1685455-2-kuba@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: b8d7519352ba ("net: shaper: rework the VALID marking (again)") Signed-off-by: Sasha Levin --- net/shaper/shaper.c | 53 ++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index fa8520025282..a28096b150b3 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -136,35 +136,58 @@ static int net_shaper_fill_handle(struct sk_buff *msg, return -EMSGSIZE; } +static void net_shaper_copy(struct net_shaper *dst, + const struct net_shaper *src) +{ + WRITE_ONCE(dst->parent.scope, READ_ONCE(src->parent.scope)); + WRITE_ONCE(dst->parent.id, READ_ONCE(src->parent.id)); + WRITE_ONCE(dst->handle.scope, READ_ONCE(src->handle.scope)); + WRITE_ONCE(dst->handle.id, READ_ONCE(src->handle.id)); + + WRITE_ONCE(dst->metric, READ_ONCE(src->metric)); + WRITE_ONCE(dst->bw_min, READ_ONCE(src->bw_min)); + WRITE_ONCE(dst->bw_max, READ_ONCE(src->bw_max)); + WRITE_ONCE(dst->burst, READ_ONCE(src->burst)); + WRITE_ONCE(dst->priority, READ_ONCE(src->priority)); + WRITE_ONCE(dst->weight, READ_ONCE(src->weight)); + + /* private fields are only used on the write path under the lock */ + data_race(dst->leaves = src->leaves); +} + static int net_shaper_fill_one(struct sk_buff *msg, const struct net_shaper_binding *binding, const struct net_shaper *shaper, const struct genl_info *info) { + struct net_shaper cur; void *hdr; hdr = genlmsg_iput(msg, info); if (!hdr) return -EMSGSIZE; + /* Make a copy to avoid data races */ + net_shaper_copy(&cur, shaper); + if (net_shaper_fill_binding(msg, binding, NET_SHAPER_A_IFINDEX) || - net_shaper_fill_handle(msg, &shaper->parent, + net_shaper_fill_handle(msg, &cur.parent, NET_SHAPER_A_PARENT) || - net_shaper_fill_handle(msg, &shaper->handle, + net_shaper_fill_handle(msg, &cur.handle, NET_SHAPER_A_HANDLE) || - ((shaper->bw_min || shaper->bw_max || shaper->burst) && - nla_put_u32(msg, NET_SHAPER_A_METRIC, shaper->metric)) || - (shaper->bw_min && - nla_put_uint(msg, NET_SHAPER_A_BW_MIN, shaper->bw_min)) || - (shaper->bw_max && - nla_put_uint(msg, NET_SHAPER_A_BW_MAX, shaper->bw_max)) || - (shaper->burst && - nla_put_uint(msg, NET_SHAPER_A_BURST, shaper->burst)) || - (shaper->priority && - nla_put_u32(msg, NET_SHAPER_A_PRIORITY, shaper->priority)) || - (shaper->weight && - nla_put_u32(msg, NET_SHAPER_A_WEIGHT, shaper->weight))) + ((cur.bw_min || cur.bw_max || cur.burst) && + nla_put_u32(msg, NET_SHAPER_A_METRIC, cur.metric)) || + (cur.bw_min && + nla_put_uint(msg, NET_SHAPER_A_BW_MIN, cur.bw_min)) || + (cur.bw_max && + nla_put_uint(msg, NET_SHAPER_A_BW_MAX, cur.bw_max)) || + (cur.burst && + nla_put_uint(msg, NET_SHAPER_A_BURST, cur.burst)) || + (cur.priority && + nla_put_u32(msg, NET_SHAPER_A_PRIORITY, cur.priority)) || + (cur.weight && + nla_put_u32(msg, NET_SHAPER_A_WEIGHT, cur.weight))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -422,7 +445,7 @@ static void net_shaper_commit(struct net_shaper_binding *binding, /* Successful update: drop the tentative mark * and update the hierarchy container. */ - *cur = shapers[i]; + net_shaper_copy(cur, &shapers[i]); smp_wmb(); __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); } From 2417df5e7bb4184b9d3a2988036bf2c46e594545 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 15 May 2026 15:13:25 -0700 Subject: [PATCH 355/697] net: shaper: rework the VALID marking (again) [ Upstream commit b8d7519352ba8c6df83259295d4a3bad093cae90 ] Recent commit changed the semantics from NOT_VALID to VALID. I didn't realize that the flags are not stored atomically with the entry in XArray. There's still a race of reader observing a VALID mark for a slot, getting interrupted, writer replacing the entry with a different one, reader continuing, fetching the entry which is now a different pointer than the pointer for which VALID was meant. The biggest consequence of this is that we may see a UAF since net_shaper_rollback() assumed that entries without VALID can be freed without observing RCU. Looks like the XArray marks are buying us nothing at this point. Let's convert the code to an explicit valid field. The smp_load_acquire() / smp_store_release() barriers are marginally cleaner. Reported-by: Sashiko Fixes: 93954b40f6a4 ("net-shapers: implement NL set and delete operations") Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260515221325.1685455-3-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/net_shaper.h | 1 + net/shaper/shaper.c | 45 ++++++++++++++++------------------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/include/net/net_shaper.h b/include/net/net_shaper.h index 5c3f49b52fe9..3939b816b001 100644 --- a/include/net/net_shaper.h +++ b/include/net/net_shaper.h @@ -53,6 +53,7 @@ struct net_shaper { /* private: */ u32 leaves; /* accounted only for NODE scope */ + bool valid; struct rcu_head rcu; }; diff --git a/net/shaper/shaper.c b/net/shaper/shaper.c index a28096b150b3..ce353042cfa1 100644 --- a/net/shaper/shaper.c +++ b/net/shaper/shaper.c @@ -304,31 +304,24 @@ static void net_shaper_default_parent(const struct net_shaper_handle *handle, parent->id = 0; } -/* MARK_0 is already in use due to XA_FLAGS_ALLOC. The VALID mark is set on - * an entry only after the device-side configuration has completed - * successfully (see net_shaper_commit()). Lookups and dumps must filter on - * this mark to avoid exposing tentative entries inserted by - * net_shaper_pre_insert() while the driver call is still in flight. - */ -#define NET_SHAPER_VALID XA_MARK_1 - static struct net_shaper * net_shaper_lookup(struct net_shaper_binding *binding, const struct net_shaper_handle *handle) { u32 index = net_shaper_handle_to_index(handle); struct net_shaper_hierarchy *hierarchy; + struct net_shaper *cur; hierarchy = net_shaper_hierarchy_rcu(binding); - if (!hierarchy || !xa_get_mark(&hierarchy->shapers, index, - NET_SHAPER_VALID)) + if (!hierarchy) return NULL; - /* Pairs with smp_wmb() in net_shaper_commit(): if the entry is - * valid, its contents must be visible too. - */ - smp_rmb(); - return xa_load(&hierarchy->shapers, index); + cur = xa_load(&hierarchy->shapers, index); + /* Check valid before reading fields */ + if (!cur || !smp_load_acquire(&cur->valid)) + return NULL; + + return cur; } /* Allocate on demand the per device shaper's hierarchy container. @@ -442,12 +435,10 @@ static void net_shaper_commit(struct net_shaper_binding *binding, if (WARN_ON_ONCE(!cur)) continue; - /* Successful update: drop the tentative mark - * and update the hierarchy container. - */ + /* Successful update: update the hierarchy container... */ net_shaper_copy(cur, &shapers[i]); - smp_wmb(); - __xa_set_mark(&hierarchy->shapers, index, NET_SHAPER_VALID); + /* ... publish to lockless readers. */ + smp_store_release(&cur->valid, true); } xa_unlock(&hierarchy->shapers); } @@ -464,10 +455,10 @@ static void net_shaper_rollback(struct net_shaper_binding *binding) xa_lock(&hierarchy->shapers); xa_for_each(&hierarchy->shapers, index, cur) { - if (xa_get_mark(&hierarchy->shapers, index, NET_SHAPER_VALID)) + if (cur->valid) continue; __xa_erase(&hierarchy->shapers, index); - kfree(cur); + kfree_rcu(cur, rcu); } xa_unlock(&hierarchy->shapers); } @@ -880,12 +871,12 @@ int net_shaper_nl_get_dumpit(struct sk_buff *skb, goto out_unlock; for (; (shaper = xa_find(&hierarchy->shapers, &ctx->start_index, - U32_MAX, NET_SHAPER_VALID)); + U32_MAX, XA_PRESENT)); ctx->start_index++) { - /* Pairs with smp_wmb() in net_shaper_commit(): the entry - * is marked VALID, so its contents must be visible too. - */ - smp_rmb(); + /* Check valid before reading fields */ + if (!smp_load_acquire(&shaper->valid)) + continue; + ret = net_shaper_fill_one(skb, binding, shaper, info); if (ret) break; From 585f9f6aef5c4542ac9d6ec45cd7dbc7df9af3ff Mon Sep 17 00:00:00 2001 From: David Howells Date: Sat, 16 May 2026 00:05:13 +0100 Subject: [PATCH 356/697] crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks [ Upstream commit 2b50aceafe6606ea52ed42aadd1b4d44a188aade ] Change the krb5 crypto library to provide facilities to precheck the length of the message about to be decrypted or verified. Fix AF_RXRPC to make use of this to validate DATA packets secured with RxGK. Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)") Closes: https://sashiko.dev/#/patchset/20260511160753.607296-1-dhowells%40redhat.com Signed-off-by: David Howells cc: Herbert Xu cc: Simon Horman cc: Chuck Lever cc: linux-afs@lists.infradead.org Reviewed-by: Jeffrey Altman Tested-by: Marc Dionne Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- Documentation/crypto/krb5.rst | 17 ++++++++--- crypto/krb5/krb5_api.c | 54 +++++++++++++++++++++++++++++++---- include/crypto/krb5.h | 9 ++++-- include/trace/events/rxrpc.h | 1 + net/rxrpc/rxgk.c | 15 ++++++++-- 5 files changed, 81 insertions(+), 15 deletions(-) diff --git a/Documentation/crypto/krb5.rst b/Documentation/crypto/krb5.rst index beffa0133446..f62e07ac6811 100644 --- a/Documentation/crypto/krb5.rst +++ b/Documentation/crypto/krb5.rst @@ -158,13 +158,22 @@ returned. When a message has been received, the location and size of the data with the message can be determined by calling:: - void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, - enum krb5_crypto_mode mode, - size_t *_offset, size_t *_len); + int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_offset, size_t *_len); The caller provides the offset and length of the message to the function, which then alters those values to indicate the region containing the data (plus any -padding). It is up to the caller to determine how much padding there is. +padding). It is up to the caller to determine how much padding there is. The +function returns an error if the length is too small or if the mode is +unsupported. An additional function:: + + int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t len, size_t min_content); + +is provided to just do a basic check that the decrypted/verified message would +have a sufficient minimum payload. Preparation Functions --------------------- diff --git a/crypto/krb5/krb5_api.c b/crypto/krb5/krb5_api.c index 23026d4206c8..c7ea40f900a7 100644 --- a/crypto/krb5/krb5_api.c +++ b/crypto/krb5/krb5_api.c @@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data); * Find the offset and size of the data in a secure message so that this * information can be used in the metadata buffer which will get added to the * digest by crypto_krb5_verify_mic(). + * + * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if + * the mode is unsupported. */ -void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, - enum krb5_crypto_mode mode, - size_t *_offset, size_t *_len) +int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_offset, size_t *_len) { switch (mode) { case KRB5_CHECKSUM_MODE: + if (*_len < krb5->cksum_len) + return -EBADMSG; *_offset += krb5->cksum_len; *_len -= krb5->cksum_len; - return; + return 0; case KRB5_ENCRYPT_MODE: + if (*_len < krb5->conf_len + krb5->cksum_len) + return -EBADMSG; *_offset += krb5->conf_len; *_len -= krb5->conf_len + krb5->cksum_len; - return; + return 0; default: WARN_ON_ONCE(1); - return; + return -EINVAL; } } EXPORT_SYMBOL(crypto_krb5_where_is_the_data); +/** + * crypto_krb5_check_data_len - Check a message is big enough + * @krb5: The encoding to use. + * @mode: Mode of operation. + * @len: The length of the secure blob. + * @min_content: Minimum length of the content inside the blob. + * + * Check that a message is large enough to hold whatever bits the encryption + * type wants to glue on (nonce, checksum) plus a minimum amount of content. + * + * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if + * the mode is unsupported. + */ +int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t len, size_t min_content) +{ + switch (mode) { + case KRB5_CHECKSUM_MODE: + if (len < krb5->cksum_len || + len - krb5->cksum_len < min_content) + return -EBADMSG; + return 0; + case KRB5_ENCRYPT_MODE: + if (len < krb5->conf_len + krb5->cksum_len || + len - (krb5->conf_len + krb5->cksum_len) < min_content) + return -EBADMSG; + return 0; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } +} +EXPORT_SYMBOL(crypto_krb5_check_data_len); + /* * Prepare the encryption with derived key data. */ diff --git a/include/crypto/krb5.h b/include/crypto/krb5.h index 71dd38f59be1..aac3ecf88467 100644 --- a/include/crypto/krb5.h +++ b/include/crypto/krb5.h @@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5, size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5, enum krb5_crypto_mode mode, size_t *_buffer_size, size_t *_offset); -void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, - enum krb5_crypto_mode mode, - size_t *_offset, size_t *_len); +int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t *_offset, size_t *_len); +int crypto_krb5_check_data_len(const struct krb5_enctype *krb5, + enum krb5_crypto_mode mode, + size_t len, size_t min_content); struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5, const struct krb5_buffer *TK, u32 usage, gfp_t gfp); diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h index 573f2df3a2c9..704a10de6670 100644 --- a/include/trace/events/rxrpc.h +++ b/include/trace/events/rxrpc.h @@ -71,6 +71,7 @@ EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \ EM(rxkad_abort_resp_version, "rxkad-resp-version") \ /* RxGK security errors */ \ + EM(rxgk_abort_1_short_header, "rxgk1-short-hdr") \ EM(rxgk_abort_1_verify_mic_eproto, "rxgk1-vfy-mic-eproto") \ EM(rxgk_abort_2_decrypt_eproto, "rxgk2-dec-eproto") \ EM(rxgk_abort_2_short_data, "rxgk2-short-data") \ diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index c39f5066d8e8..04a761c79548 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -480,8 +480,12 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, _enter(""); - crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, - &data_offset, &data_len); + if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE, + &data_offset, &data_len) < 0) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_1_short_header); + goto put_gk; + } hdr = kzalloc(sizeof(*hdr), GFP_NOFS); if (!hdr) @@ -529,6 +533,13 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, _enter(""); + if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE, + len, sizeof(hdr)) < 0) { + ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, + rxgk_abort_2_short_header); + goto error; + } + ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); if (ret < 0) { if (ret != -ENOMEM) From 26f1d4522060ee1f16f1165d29a7aa2926bd21ff Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 16 May 2026 14:26:16 -0700 Subject: [PATCH 357/697] net: ag71xx: check error for platform_get_irq [ Upstream commit e7c70bf97e90d974cd575e4c90f8f9b07d056da3 ] Complete error handling for a failed platform_get_irq() call Fixes: d51b6ce441d3 ("net: ethernet: add ag71xx driver") Signed-off-by: Rosen Penev Reviewed-by: Oleksij Rempel Link: https://patch.msgid.link/20260516212616.11758-1-rosenp@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/atheros/ag71xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/atheros/ag71xx.c b/drivers/net/ethernet/atheros/ag71xx.c index cbc730c7cff2..8e6bd99b22d7 100644 --- a/drivers/net/ethernet/atheros/ag71xx.c +++ b/drivers/net/ethernet/atheros/ag71xx.c @@ -1856,6 +1856,9 @@ static int ag71xx_probe(struct platform_device *pdev) ag71xx_int_disable(ag, AG71XX_INT_POLL); ndev->irq = platform_get_irq(pdev, 0); + if (ndev->irq < 0) + return ndev->irq; + err = devm_request_irq(&pdev->dev, ndev->irq, ag71xx_interrupt, 0x0, dev_name(&pdev->dev), ndev); if (err) { From 1861d369efd62d67796563bf3e01fc22e5626f8b Mon Sep 17 00:00:00 2001 From: Xingwang Xiang Date: Sun, 17 May 2026 23:56:26 +0900 Subject: [PATCH 358/697] bpf, skmsg: fix verdict sk_data_ready racing with ktls rx [ Upstream commit ddf8029623a1af20e984c040e89ff918158397ab ] sk_psock_strp_data_ready() already checks tls_sw_has_ctx_rx() and defers to psock->saved_data_ready when a TLS RX context is present, avoiding a conflict with the TLS strparser's ownership of the receive queue (commit e91de6afa81c, "bpf: Fix running sk_skb program types with ktls"). sk_psock_verdict_data_ready() has no equivalent guard. When a socket is inserted into a sockmap (BPF_SK_SKB_VERDICT) before TLS RX is configured, tls_sw_strparser_arm() saves sk_psock_verdict_data_ready as rx_ctx->saved_data_ready. On data arrival: tls_data_ready -> tls_strp_data_ready -> tls_rx_msg_ready -> saved_data_ready() = sk_psock_verdict_data_ready() -> tcp_read_skb() drains sk_receive_queue via __skb_unlink() without calling tcp_eat_skb(), so copied_seq is not advanced. tls_strp_msg_load() then finds tcp_inq() >= full_len (stale), calls tcp_recv_skb() on the now-empty queue, hits WARN_ON_ONCE(!first), and returns with rx_ctx->strp.anchor.frag_list pointing at a psock-owned (potentially freed) skb. tls_decrypt_sg() subsequently walks that frag_list: use-after-free. Apply the same fix as sk_psock_strp_data_ready(): if a TLS RX context is present, call psock->saved_data_ready (sock_def_readable) to wake recv() waiters and return immediately, leaving the receive queue untouched. TLS retains sole ownership of the queue and decrypts the record normally through tls_sw_recvmsg(). Fixes: ef5659280eb1 ("bpf, sockmap: Allow skipping sk_skb parser program") Signed-off-by: Xingwang Xiang Link: https://patch.msgid.link/20260517145630.20521-2-v3rdant.xiang@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/skmsg.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/core/skmsg.c b/net/core/skmsg.c index 35a6acbf9a57..75ea4fdb2764 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -1268,12 +1268,19 @@ static int sk_psock_verdict_recv(struct sock *sk, struct sk_buff *skb) static void sk_psock_verdict_data_ready(struct sock *sk) { const struct proto_ops *ops = NULL; + struct sk_psock *psock; struct socket *sock; int copied; trace_sk_data_ready(sk); rcu_read_lock(); + psock = sk_psock(sk); + if (psock && tls_sw_has_ctx_rx(sk)) { + psock->saved_data_ready(sk); + rcu_read_unlock(); + return; + } sock = READ_ONCE(sk->sk_socket); if (likely(sock)) ops = READ_ONCE(sock->ops); @@ -1283,8 +1290,6 @@ static void sk_psock_verdict_data_ready(struct sock *sk) copied = ops->read_skb(sk, sk_psock_verdict_recv); if (copied >= 0) { - struct sk_psock *psock; - rcu_read_lock(); psock = sk_psock(sk); if (psock) From e47f7060eaf60894e3e4d0e3c4fe6e1f2eacfbdd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 May 2026 08:46:11 +0000 Subject: [PATCH 359/697] tcp: fix stale per-CPU tcp_tw_isn leak enabling ISN prediction [ Upstream commit 1bbf0ced1d9db73ac7893c2187f3459288603e0d ] Blamed commit moved the TIME_WAIT-derived ISN from the skb control block to a per-CPU variable, assuming the value would always be consumed by tcp_conn_request() for the same packet that wrote it. That assumption is violated by multiple drop paths between the producer (__this_cpu_write(tcp_tw_isn, isn) in tcp_v{4,6}_rcv()) and the consumer (tcp_conn_request()): - min_ttl / min_hopcount check - xfrm policy check - tcp_inbound_hash() MD5/AO mismatch - tcp_filter() eBPF/SO_ATTACH_FILTER drop - th->syn && th->fin discard in tcp_rcv_state_process() TCP_LISTEN - psp_sk_rx_policy_check() in tcp_v{4,6}_do_rcv() - tcp_checksum_complete() in tcp_v{4,6}_do_rcv() - tcp_v{4,6}_cookie_check() returning NULL When a packet is dropped on any of these paths, tcp_tw_isn is left set. The next SYN processed on the same CPU then consumes the non zero value in tcp_conn_request(), receiving a potentially predictable ISN. This patch moves back tcp_tw_isn to skb->cb[], getting rid of the per-cpu variable. Note that tcp_v{4,6}_fill_cb() do not set it. Very litle impact on overall code size/complexity: $ scripts/bloat-o-meter -t vmlinux.old vmlinux.new add/remove: 0/0 grow/shrink: 2/1 up/down: 8/-15 (-7) Function old new delta tcp_v6_rcv 3038 3042 +4 tcp_v4_rcv 3035 3039 +4 tcp_conn_request 2938 2923 -15 Total: Before=24436060, After=24436053, chg -0.00% Fixes: 41eecbd712b7 ("tcp: replace TCP_SKB_CB(skb)->tcp_tw_isn with a per-cpu field") Reported-by: Chris Mason Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260519084611.2485277-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- include/net/tcp.h | 7 ++++--- net/ipv4/tcp.c | 3 --- net/ipv4/tcp_input.c | 15 ++++++--------- net/ipv4/tcp_ipv4.c | 3 ++- net/ipv6/tcp_ipv6.c | 3 ++- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index cf507b989bff..f460d2c391de 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -65,8 +65,6 @@ static inline void tcp_orphan_count_dec(void) this_cpu_dec(tcp_orphan_count); } -DECLARE_PER_CPU(u32, tcp_tw_isn); - void tcp_time_wait(struct sock *sk, int state, int timeo); #define MAX_TCP_HEADER L1_CACHE_ALIGN(128 + MAX_HEADER) @@ -1028,10 +1026,13 @@ struct tcp_skb_cb { __u32 seq; /* Starting sequence number */ __u32 end_seq; /* SEQ + FIN + SYN + datalen */ union { - /* Note : + /* Notes : + * tcp_tw_isn is used in input path only + * (isn chosen by tcp_timewait_state_process()) * tcp_gso_segs/size are used in write queue only, * cf tcp_skb_pcount()/tcp_skb_mss() */ + u32 tcp_tw_isn; struct { u16 tcp_gso_segs; u16 tcp_gso_size; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 2de4748269ca..6fc00b38695b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -300,9 +300,6 @@ enum { DEFINE_PER_CPU(unsigned int, tcp_orphan_count); EXPORT_PER_CPU_SYMBOL_GPL(tcp_orphan_count); -DEFINE_PER_CPU(u32, tcp_tw_isn); -EXPORT_PER_CPU_SYMBOL_GPL(tcp_tw_isn); - long sysctl_tcp_mem[3] __read_mostly; EXPORT_IPV6_MOD(sysctl_tcp_mem); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b5cf32a56c04..c650398e9199 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -7387,6 +7387,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, struct sock *sk, struct sk_buff *skb) { struct tcp_fastopen_cookie foc = { .len = -1 }; + u32 isn = TCP_SKB_CB(skb)->tcp_tw_isn; struct tcp_options_received tmp_opt; const struct tcp_sock *tp = tcp_sk(sk); struct net *net = sock_net(sk); @@ -7397,20 +7398,16 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, struct dst_entry *dst; struct flowi fl; u8 syncookies; - u32 isn; #ifdef CONFIG_TCP_AO const struct tcp_ao_hdr *aoh; #endif - isn = __this_cpu_read(tcp_tw_isn); - if (isn) { - /* TW buckets are converted to open requests without - * limitations, they conserve resources and peer is - * evidently real one. - */ - __this_cpu_write(tcp_tw_isn, 0); - } else { + /* If isn is non-zero, this SYN originally matched a TIME_WAIT socket. + * TW sockets are converted to open requests without limitations, + * we skip the queue limits and syncookie checks in the block below. + */ + if (!isn) { syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); if (syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 702fdff58f7a..36206fc6aed2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2333,6 +2333,7 @@ int tcp_v4_rcv(struct sk_buff *skb) } } + isn = 0; process: if (static_branch_unlikely(&ip4_min_ttl)) { /* min_ttl can be changed concurrently from do_ip_setsockopt() */ @@ -2361,6 +2362,7 @@ int tcp_v4_rcv(struct sk_buff *skb) th = (const struct tcphdr *)skb->data; iph = ip_hdr(skb); tcp_v4_fill_cb(skb, iph, th); + TCP_SKB_CB(skb)->tcp_tw_isn = isn; skb->dev = NULL; @@ -2446,7 +2448,6 @@ int tcp_v4_rcv(struct sk_buff *skb) sk = sk2; tcp_v4_restore_cb(skb); refcounted = false; - __this_cpu_write(tcp_tw_isn, isn); goto process; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 7f20db11e8ce..59b5900dd42b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1863,6 +1863,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) } } + isn = 0; process: if (static_branch_unlikely(&ip6_min_hopcount)) { /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ @@ -1891,6 +1892,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) th = (const struct tcphdr *)skb->data; hdr = ipv6_hdr(skb); tcp_v6_fill_cb(skb, hdr, th); + TCP_SKB_CB(skb)->tcp_tw_isn = isn; skb->dev = NULL; @@ -1978,7 +1980,6 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) sk = sk2; tcp_v6_restore_cb(skb); refcounted = false; - __this_cpu_write(tcp_tw_isn, isn); goto process; } From 4669f84adcb1e0567bd9c2e1cae827b63c53103b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 21 May 2026 10:42:16 +0200 Subject: [PATCH 360/697] gpio: cdev: check if uAPI v2 config attributes are correctly zeroed [ Upstream commit 3e6ccd790ed69bedd3d9626d01dd35cf9821c121 ] We check the padding of other uAPI v2 structures but not that of line config attributes. For used attributes: check if their padding is zeroed, for unused: check if the entire structure is zeroed. Fixes: 3c0d9c635ae2 ("gpiolib: cdev: support GPIO_V2_GET_LINE_IOCTL and GPIO_V2_LINE_GET_VALUES_IOCTL") Reviewed-by: Kent Gibson Link: https://patch.msgid.link/20260521-gpio-cdev-attr-padding-check-v3-1-ec3bcbe2e358@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpiolib-cdev.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 986312c71678..89abf7a238d8 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1208,6 +1208,7 @@ static int gpio_v2_line_flags_validate(u64 flags) static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, unsigned int num_lines) { + size_t unused_attrs; unsigned int i; u64 flags; int ret; @@ -1215,9 +1216,21 @@ static int gpio_v2_line_config_validate(struct gpio_v2_line_config *lc, if (lc->num_attrs > GPIO_V2_LINE_NUM_ATTRS_MAX) return -EINVAL; + unused_attrs = GPIO_V2_LINE_NUM_ATTRS_MAX - lc->num_attrs; + if (!mem_is_zero(lc->padding, sizeof(lc->padding))) return -EINVAL; + for (i = 0; i < lc->num_attrs; i++) { + if (lc->attrs[i].attr.padding != 0) + return -EINVAL; + } + + if (unused_attrs) { + if (!mem_is_zero(&lc->attrs[lc->num_attrs], unused_attrs * sizeof(*lc->attrs))) + return -EINVAL; + } + for (i = 0; i < num_lines; i++) { flags = gpio_v2_line_config_flags(lc, i); ret = gpio_v2_line_flags_validate(flags); From ea28b286649b70618e9dd3e895812417a7712a11 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 20 May 2026 10:49:11 +0200 Subject: [PATCH 361/697] gpio: aggregator: fix a potential use-after-free [ Upstream commit 30c073cab97afb31901f94de9605177b6b84367e ] On error we free aggr->lookups->dev_id before removing the entry from the lookup table. If a concurrent thread calls gpiod_find() before we remove the entry, it could iterate over the list and call gpiod_match_lookup_table() which unconditionally dereferences dev_id when calling strcmp(). Reverse the order of cleanup. Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260520084911.27938-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-aggregator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 416f265d09d0..a68665700733 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -970,8 +970,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) return 0; err_remove_lookup_table: - kfree(aggr->lookups->dev_id); gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); err_remove_swnode: fwnode_remove_software_node(swnode); err_remove_lookups: From 80d94cf1773a367f6c83ffc2a09dfc01fdf64de7 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 27 Mar 2026 11:31:12 +0100 Subject: [PATCH 362/697] gpio: aggregator: stop using dev-sync-probe [ Upstream commit 3a27f40b457053e6112a63d14590e4a3ff553b44 ] dev-err-probe is an overengineered solution to a simple problem. Use a combination of wait_for_probe() and device_is_bound() to synchronously wait for the platform device to probe. Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260327-gpio-kill-dev-sync-probe-v1-2-efac254f1a1d@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Stable-dep-of: 61fef83f239e ("gpio: aggregator: remove the software node when deactivating the aggregator") Signed-off-by: Sasha Levin --- drivers/gpio/Kconfig | 1 - drivers/gpio/gpio-aggregator.c | 38 +++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4d644dcecad9..3b7284938bab 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1968,7 +1968,6 @@ menu "Virtual GPIO drivers" config GPIO_AGGREGATOR tristate "GPIO Aggregator" select CONFIGFS_FS - select DEV_SYNC_PROBE help Say yes here to enable the GPIO Aggregator, which provides a way to aggregate existing GPIO lines into a new virtual GPIO chip. diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index a68665700733..23987c38a246 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -32,8 +32,6 @@ #include #include -#include "dev-sync-probe.h" - #define AGGREGATOR_MAX_GPIOS 512 #define AGGREGATOR_LEGACY_PREFIX "_sysfs" @@ -42,7 +40,7 @@ */ struct gpio_aggregator { - struct dev_sync_probe_data probe_data; + struct platform_device *pdev; struct config_group group; struct gpiod_lookup_table *lookups; struct mutex lock; @@ -135,7 +133,7 @@ static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); - return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); + return aggr->pdev && platform_get_drvdata(aggr->pdev); } /* Only aggregators created via legacy sysfs can be "activating". */ @@ -143,7 +141,7 @@ static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) { lockdep_assert_held(&aggr->lock); - return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); + return aggr->pdev && !platform_get_drvdata(aggr->pdev); } static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) @@ -909,6 +907,7 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) { struct platform_device_info pdevinfo; struct gpio_aggregator_line *line; + struct platform_device *pdev; struct fwnode_handle *swnode; unsigned int n = 0; int ret = 0; @@ -963,12 +962,23 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) gpiod_add_lookup_table(aggr->lookups); - ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); - if (ret) + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) { + ret = PTR_ERR(pdev); goto err_remove_lookup_table; + } + wait_for_device_probe(); + if (!device_is_bound(&pdev->dev)) { + ret = -ENXIO; + goto err_unregister_pdev; + } + + aggr->pdev = pdev; return 0; +err_unregister_pdev: + platform_device_unregister(pdev); err_remove_lookup_table: gpiod_remove_lookup_table(aggr->lookups); kfree(aggr->lookups->dev_id); @@ -982,7 +992,8 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) { - dev_sync_probe_unregister(&aggr->probe_data); + platform_device_unregister(aggr->pdev); + aggr->pdev = NULL; gpiod_remove_lookup_table(aggr->lookups); kfree(aggr->lookups->dev_id); kfree(aggr->lookups); @@ -1146,7 +1157,7 @@ gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) guard(mutex)(&aggr->lock); - pdev = aggr->probe_data.pdev; + pdev = aggr->pdev; if (pdev) return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); @@ -1323,7 +1334,6 @@ gpio_aggregator_make_group(struct config_group *group, const char *name) return ERR_PTR(ret); config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); - dev_sync_probe_init(&aggr->probe_data); return &aggr->group; } @@ -1473,12 +1483,6 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); - /* - * Since the device created by sysfs might be toggled via configfs - * 'live' attribute later, this initialization is needed. - */ - dev_sync_probe_init(&aggr->probe_data); - /* Expose to configfs */ res = configfs_register_group(&gpio_aggregator_subsys.su_group, &aggr->group); @@ -1497,7 +1501,7 @@ static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, goto remove_table; } - aggr->probe_data.pdev = pdev; + aggr->pdev = pdev; module_put(THIS_MODULE); return count; From 3e657619cf7258cb53b1beaf0d02998297695cde Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 20 May 2026 14:16:31 +0200 Subject: [PATCH 363/697] gpio: aggregator: remove the software node when deactivating the aggregator [ Upstream commit 61fef83f239ecace1cce716135762a2d9b7b1fc6 ] The dynamic software node we create for the aggregator platform device when using configfs is leaked when the device is deactivated. Destroy it as the last step in the tear-down path. Fixes: 86f162e73d2d ("gpio: aggregator: introduce basic configfs interface") Reported-by: Geert Uytterhoeven Closes: https://lore.kernel.org/all/CAMuHMdVZ=XUvJTGdDAjnkxgtw7Uvnn61iOy3XN_5XNZM2anctw@mail.gmail.com/ Link: https://patch.msgid.link/20260520121631.33976-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-aggregator.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 23987c38a246..8cbebcb4bb1f 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -992,11 +992,15 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) { + struct fwnode_handle *swnode; + + swnode = dev_fwnode(&aggr->pdev->dev); platform_device_unregister(aggr->pdev); aggr->pdev = NULL; gpiod_remove_lookup_table(aggr->lookups); kfree(aggr->lookups->dev_id); kfree(aggr->lookups); + fwnode_remove_software_node(swnode); } static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, From decacc6308c539db7b7152afd42b232aaf816ac9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 18 May 2026 11:53:18 +0200 Subject: [PATCH 364/697] gpio: aggregator: lock device when calling device_is_bound() [ Upstream commit 598a2b3e2e0e6aa2e9f7843c96c45b5ea11e0411 ] The kerneldoc for device_is_bound() says it must be called with the device lock taken. Add missing synchronization to this driver. Fixes: 3a27f40b4570 ("gpio: aggregator: stop using dev-sync-probe") Link: https://patch.msgid.link/20260518-gpio-dev-lock-v1-2-cc4736f3ff0b@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-aggregator.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index 8cbebcb4bb1f..17db07cf92d0 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -969,9 +969,12 @@ static int gpio_aggregator_activate(struct gpio_aggregator *aggr) } wait_for_device_probe(); - if (!device_is_bound(&pdev->dev)) { - ret = -ENXIO; - goto err_unregister_pdev; + + scoped_guard(device, &pdev->dev) { + if (!device_is_bound(&pdev->dev)) { + ret = -ENXIO; + goto err_unregister_pdev; + } } aggr->pdev = pdev; From db86ac6d8dafea982967e1bde249df8545af67ac Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Thu, 21 May 2026 13:30:57 +0100 Subject: [PATCH 365/697] ASoC: cs35l56: Fix flushing of IRQ work in cs35l56_sdw_remove() [ Upstream commit 18e7bd9f2446664053f8c34b72abd4606d22d858 ] Use flush_work() instead of cancel_work_sync() to terminate pending IRQ work in cs35l56_sdw_remove(). And flush_work() again after masking the interrupts to flush any queueing that was racing with the masking. This is the same sequence as cs35l56_sdw_system_suspend(). cs35l56_sdw_interrupt() takes the pm_runtime to prevent the bus powering- down before the interrupt status can be read and handled. The work releases this pm_runtime. So cancelling it, instead of flushing, could leave an unbalanced pm_runtime. Signed-off-by: Richard Fitzgerald Fixes: e49611252900 ("ASoC: cs35l56: Add driver for Cirrus Logic CS35L56") Link: https://patch.msgid.link/20260521123057.988732-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/cs35l56-sdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 42d24ac2977f..a513a15d7e5a 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -560,10 +560,11 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) /* Disable SoundWire interrupts */ cs35l56->sdw_irq_no_unmask = true; - cancel_work_sync(&cs35l56->sdw_irq_work); + flush_work(&cs35l56->sdw_irq_work); sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + flush_work(&cs35l56->sdw_irq_work); cs35l56_remove(cs35l56); From 04ef7592eaadd9ca8f8f66e76452f73525cff819 Mon Sep 17 00:00:00 2001 From: Shuicheng Lin Date: Thu, 14 May 2026 20:32:10 +0000 Subject: [PATCH 366/697] drm/xe/oa: Fix exec_queue leak on width check in stream open [ Upstream commit 4d25342543c01310fc4e0cba7cb17c775e2421e2 ] In xe_oa_stream_open_ioctl(), when param.exec_q->width > 1 the function returns -EOPNOTSUPP directly, skipping the existing err_exec_q cleanup path. The exec_queue reference obtained by xe_exec_queue_lookup() is leaked. The exec queue holds a reference on the xe_file, which is only dropped during queue teardown. The leaked lookup ref is not on the file's exec_queue xarray, so file close cannot release it. This keeps both the exec queue and the file private state pinned indefinitely. Jump to err_exec_q instead of returning directly so the reference is released. Fixes: f0ed39830e60 ("xe/oa: Fix query mode of operation for OAR/OAC") Assisted-by: Claude:claude-opus-4.6 Reviewed-by: Ashutosh Dixit Link: https://patch.msgid.link/20260514203210.593488-1-shuicheng.lin@intel.com Signed-off-by: Shuicheng Lin (cherry picked from commit 339fa0be9e4a5d69fa47e91f4a36574224fb478f) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_oa.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 98bfb127eafc..7d04591e297a 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -2043,8 +2043,10 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f if (XE_IOCTL_DBG(oa->xe, !param.exec_q)) return -ENOENT; - if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) - return -EOPNOTSUPP; + if (XE_IOCTL_DBG(oa->xe, param.exec_q->width > 1)) { + ret = -EOPNOTSUPP; + goto err_exec_q; + } } /* From 76dd50b7888d65956ca796d9b6cdd17a9001fb79 Mon Sep 17 00:00:00 2001 From: Nimrod Oren Date: Wed, 20 May 2026 18:39:28 +0300 Subject: [PATCH 367/697] selftests: net: Fix checksums in xdp_native [ Upstream commit dfc077043351a81887d1e4c9ac244e9243f3cbf2 ] Data adjustment cases failed with "Data exchange failed" when using IPv4 because the program did not update the IP and UDP checksums in the IPv4 branch. The issue was masked when both IPv4 and IPv6 were configured, since the test harness prefers IPv6. While here, generalize csum_fold_helper() to fold twice so it works for any 32-bit input. Fixes: 0b65cfcef9c5 ("selftests: drv-net: Test tail-adjustment support") Reviewed-by: Carolina Jubran Reviewed-by: Dragos Tatulea Signed-off-by: Nimrod Oren Link: https://patch.msgid.link/20260520153928.3371765-1-noren@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- .../selftests/net/lib/xdp_native.bpf.c | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/tools/testing/selftests/net/lib/xdp_native.bpf.c b/tools/testing/selftests/net/lib/xdp_native.bpf.c index c368fc045f4b..d5134f93f7d7 100644 --- a/tools/testing/selftests/net/lib/xdp_native.bpf.c +++ b/tools/testing/selftests/net/lib/xdp_native.bpf.c @@ -268,6 +268,17 @@ static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port) return XDP_PASS; } +static __always_inline __u16 csum_fold_helper(__u32 csum) +{ + csum = (csum & 0xffff) + (csum >> 16); + return ~((csum & 0xffff) + (csum >> 16)); +} + +static __always_inline __u16 csum_fold_udp_helper(__u32 csum) +{ + return csum_fold_helper(csum) ? : 0xffff; +} + static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) { void *data_end = (void *)(long)ctx->data_end; @@ -281,21 +292,22 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) if (eth->h_proto == bpf_htons(ETH_P_IP)) { struct iphdr *iph = data + sizeof(*eth); - __u16 total_len; if (iph + 1 > (struct iphdr *)data_end) return NULL; - iph->tot_len = bpf_htons(bpf_ntohs(iph->tot_len) + offset); - udph = (void *)eth + sizeof(*iph) + sizeof(*eth); if (!udph || udph + 1 > (struct udphdr *)data_end) return NULL; - len_new = bpf_htons(bpf_ntohs(udph->len) + offset); + len = iph->tot_len; + len_new = bpf_htons(bpf_ntohs(len) + offset); + iph->tot_len = len_new; + iph->check = csum_fold_helper( + bpf_csum_diff(&len, sizeof(len), &len_new, + sizeof(len_new), ~((__u32)iph->check))); } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { struct ipv6hdr *ipv6h = data + sizeof(*eth); - __u16 payload_len; if (ipv6h + 1 > (struct ipv6hdr *)data_end) return NULL; @@ -304,33 +316,27 @@ static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum) if (!udph || udph + 1 > (struct udphdr *)data_end) return NULL; - *udp_csum = ~((__u32)udph->check); - len = ipv6h->payload_len; len_new = bpf_htons(bpf_ntohs(len) + offset); ipv6h->payload_len = len_new; - - *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, - sizeof(len_new), *udp_csum); - - len = udph->len; - len_new = bpf_htons(bpf_ntohs(udph->len) + offset); - *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, - sizeof(len_new), *udp_csum); } else { return NULL; } + len = udph->len; + len_new = bpf_htons(bpf_ntohs(len) + offset); + + *udp_csum = ~((__u32)udph->check); + *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, + sizeof(len_new), *udp_csum); + *udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new, + sizeof(len_new), *udp_csum); + udph->len = len_new; return udph; } -static __u16 csum_fold_helper(__u32 csum) -{ - return ~((csum & 0xffff) + (csum >> 16)) ? : 0xffff; -} - static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, __u32 hdr_len) { @@ -359,7 +365,7 @@ static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset, return -1; udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); - udph->check = (__u16)csum_fold_helper(udp_csum); + udph->check = (__u16)csum_fold_udp_helper(udp_csum); if (bpf_xdp_adjust_tail(ctx, 0 - offset) < 0) return -1; @@ -403,7 +409,7 @@ static int xdp_adjst_tail_grow_data(struct xdp_md *ctx, __u16 offset) return -1; udp_csum = bpf_csum_diff(0, 0, (__be32 *)tmp_buff, offset, udp_csum); - udph->check = (__u16)csum_fold_helper(udp_csum); + udph->check = (__u16)csum_fold_udp_helper(udp_csum); buff_len = bpf_xdp_get_buff_len(ctx); @@ -483,8 +489,7 @@ static int xdp_adjst_head_shrnk_data(struct xdp_md *ctx, __u64 hdr_len, return -1; udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum); - - udph->check = (__u16)csum_fold_helper(udp_csum); + udph->check = (__u16)csum_fold_udp_helper(udp_csum); if (bpf_xdp_load_bytes(ctx, 0, tmp_buff, MAX_ADJST_OFFSET) < 0) return -1; @@ -541,7 +546,7 @@ static int xdp_adjst_head_grow_data(struct xdp_md *ctx, __u64 hdr_len, return -1; udp_csum = bpf_csum_diff(0, 0, (__be32 *)data_buff, offset, udp_csum); - udph->check = (__u16)csum_fold_helper(udp_csum); + udph->check = (__u16)csum_fold_udp_helper(udp_csum); if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0) return -1; From bc0020490f8890ea83950a1f1883c7c1898a8a35 Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Wed, 20 May 2026 10:00:36 +0530 Subject: [PATCH 368/697] octeontx2-af: npc: Fix allmulticast skip logic for LBK and SDP VFs [ Upstream commit 9eddc819f00b5b74bb4ac91396f80bd35f5f3561 ] When installing the allmulticast NPC rule, rvu_npc_install_allmulti_entry() should skip LBK and SDP VFs (only CGX PF/VF may add the entry). The code combined is_lbk_vf() and is_sdp_vf() with logical AND, which is never true for a single pcifunc, so the intended early return never ran. Use logical OR instead. Cc: Geetha sowjanya Fixes: ae703539f49d2 ("octeontx2-af: Cleanup loopback device checks") Signed-off-by: Ratheesh Kannoth Link: https://patch.msgid.link/20260520043036.1523798-1-rkannoth@marvell.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 8658cb2143df..e28675fe1890 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -837,7 +837,7 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u16 vf_func; /* Only CGX PF/VF can add allmulticast entry */ - if (is_lbk_vf(rvu, pcifunc) && is_sdp_vf(rvu, pcifunc)) + if (is_lbk_vf(rvu, pcifunc) || is_sdp_vf(rvu, pcifunc)) return; blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); From fa627a5eaa83fc0261f44ef3769693b886ca6e27 Mon Sep 17 00:00:00 2001 From: Aditya Garg Date: Tue, 19 May 2026 22:15:53 -0700 Subject: [PATCH 369/697] net: mana: validate rx_req_idx to prevent out-of-bounds array access [ Upstream commit b809d0409991b75a6cff846a5ac27c3062953f84 ] In mana_hwc_rx_event_handler(), rx_req_idx is derived from sge->address in DMA-coherent memory. In Confidential VMs (SEV-SNP/TDX), this memory is shared unencrypted and HW can modify WQE contents at any time. No bounds check exists on rx_req_idx, which can lead to an out-of-bounds access into reqs[]. Add bounds check on rx_req_idx in mana_hwc_rx_event_handler() before using it to index the reqs[] array. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Signed-off-by: Aditya Garg Reviewed-by: Haiyang Zhang Link: https://patch.msgid.link/20260520051553.857120-1-gargaditya@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/hw_channel.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c index 1986bf493399..5faf4ca75b0f 100644 --- a/drivers/net/ethernet/microsoft/mana/hw_channel.c +++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c @@ -265,6 +265,12 @@ static void mana_hwc_rx_event_handler(void *ctx, u32 gdma_rxq_id, rq_base_addr = hwc_rxq->msg_buf->mem_info.dma_handle; rx_req_idx = (sge->address - rq_base_addr) / hwc->max_req_msg_size; + if (rx_req_idx >= hwc_rxq->msg_buf->num_reqs) { + dev_err(hwc->dev, "HWC RX: wrong rx_req_idx=%llu, num_reqs=%u\n", + rx_req_idx, hwc_rxq->msg_buf->num_reqs); + return; + } + rx_req = &hwc_rxq->msg_buf->reqs[rx_req_idx]; resp = (struct gdma_resp_hdr *)rx_req->buf_va; From 719007c3492f0f1f9e9cdbed8ac45ba45bb13eeb Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 20 May 2026 00:57:38 -0700 Subject: [PATCH 370/697] tap: fix stack info leak in tap_ioctl() SIOCGIFHWADDR [ Upstream commit bddc09212c24934643bd44fc794748d2bbb3b6cd ] In the SIOCGIFHWADDR path, tap_ioctl() copies 16 bytes of an uninitialised on-stack struct sockaddr_storage to userspace via ifr_hwaddr, but netif_get_mac_address() only writes sa_family and dev->addr_len (6 for Ethernet) bytes, leaving sa_data[6..13] uninitialised. Those 8 trailing bytes leak kernel stack contents; SIOCGIFHWADDR on a macvtap chardev returns kernel .text and direct-map pointers, defeating KASLR. Initialise ss at declaration. Fixes: 3b23a32a6321 ("net: fix dev_ifsioc_locked() race condition") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260520075736.3415676-3-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/tap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 1197f245e873..6fd3b14273b3 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -919,11 +919,11 @@ static long tap_ioctl(struct file *file, unsigned int cmd, struct tap_queue *q = file->private_data; struct tap_dev *tap; void __user *argp = (void __user *)arg; + struct sockaddr_storage ss = {}; struct ifreq __user *ifr = argp; unsigned int __user *up = argp; unsigned short u; int __user *sp = argp; - struct sockaddr_storage ss; int s; int ret; From d1d76bbb6d7af470fa57bf4888d4912109c72bd2 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 20 May 2026 15:12:02 +0200 Subject: [PATCH 371/697] net: airoha: Disable GDM2 forwarding before configuring GDM2 loopback [ Upstream commit 985d4a55e64e43bd86eeb896b81ceba453301989 ] Hw design requires to disable GDM2 forwarding before configuring GDM2 loopback in airoha_set_gdm2_loopback routine. Fixes: 9cd451d414f6e ("net: airoha: Add loopback support for GDM2") Tested-by: Madhur Agrawal Signed-off-by: Lorenzo Bianconi Link: https://patch.msgid.link/20260520-airoha-disable-gdm2-fwd-v1-1-1eeea5dffc2f@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/airoha/airoha_eth.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 27d62acfcc39..9781a6fc9bf9 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -1800,11 +1800,8 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) u32 val, pse_port, chan; int src_port; - /* Forward the traffic to the proper GDM port */ - pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 - : FE_PSE_PORT_GDM4; airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), - pse_port); + FE_PSE_PORT_DROP); airoha_fe_clear(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), GDM_STRIP_CRC_MASK); @@ -1822,6 +1819,11 @@ static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port) GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); + /* Forward the traffic to the proper GDM port */ + pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 + : FE_PSE_PORT_GDM4; + airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(AIROHA_GDM2_IDX), + pse_port); /* Disable VIP and IFC for GDM2 */ airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(AIROHA_GDM2_IDX)); From 8129611d4ede5c4b36a6b2649db140826fa0e569 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Wed, 20 May 2026 20:58:42 +0000 Subject: [PATCH 372/697] pds_core: ensure null-termination for firmware version strings [ Upstream commit 3d4432d34c1992701289cbe12df9fd024f315998 ] The driver passes fw_version directly to devlink_info_version_stored_put() without ensuring null-termination. While current firmware null-terminates these strings, the driver should not rely on this behavior. Add explicit null-termination to prevent potential issues if firmware behavior changes. Fixes: 45d76f492938 ("pds_core: set up device and adminq") Signed-off-by: Nikhil P. Rao Link: https://patch.msgid.link/20260520205842.1486718-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/amd/pds_core/devlink.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c index d8dc39da4161..621791a3c543 100644 --- a/drivers/net/ethernet/amd/pds_core/devlink.c +++ b/drivers/net/ethernet/amd/pds_core/devlink.c @@ -121,12 +121,14 @@ int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req, listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names)); for (i = 0; i < listlen; i++) { + char *fw_ver = fw_list.fw_names[i].fw_version; + if (i < ARRAY_SIZE(fw_slotnames)) strscpy(buf, fw_slotnames[i], sizeof(buf)); else snprintf(buf, sizeof(buf), "fw.slot_%d", i); - err = devlink_info_version_stored_put(req, buf, - fw_list.fw_names[i].fw_version); + fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0'; + err = devlink_info_version_stored_put(req, buf, fw_ver); if (err) return err; } From e334cbf3388fd9334503a778a82d9e9f14dd2f71 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Wed, 20 May 2026 22:44:42 +0200 Subject: [PATCH 373/697] net: gro: don't merge zcopy skbs [ Upstream commit 4db79a322db8c97f7b73b8a347395ef4d685eb40 ] skb_gro_receive() can currently copy frags between the source and GRO skb, without checking the zerocopy status, and in particular the SKBFL_MANAGED_FRAG_REFS flag. When SKBFL_MANAGED_FRAG_REFS is set, the skb doesn't hold a reference on the pages in shinfo->frags. Appending those frags to another skb's frags without fixing up the page refcount can lead to UAF. When either the last skb in the GRO chain (the one we would append frags to) or the source skb is zerocopy, don't merge the skbs. Fixes: 753f1ca4e1e5 ("net: introduce managed frags infrastructure") Reported-by: Huzaifa Sidhpurwala Signed-off-by: Sabrina Dubroca Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/c3b7f906bbfcbdfd7b4fa9d6c18a438870df85be.1779307748.git.sd@queasysnail.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/gro.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/gro.c b/net/core/gro.c index 867611d171db..b5f790a643d4 100644 --- a/net/core/gro.c +++ b/net/core/gro.c @@ -109,6 +109,9 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb) if (p->pp_recycle != skb->pp_recycle) return -ETOOMANYREFS; + if (skb_zcopy(p) || skb_zcopy(skb)) + return -ETOOMANYREFS; + if (unlikely(p->len + len >= netif_get_gro_max_size(p->dev, p) || NAPI_GRO_CB(skb)->flush)) return -E2BIG; From 6836f694126e5dfb44da4f77706ae29e45cc2e1e Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 20 May 2026 20:00:44 +0200 Subject: [PATCH 374/697] io_uring/nop: pass all errors to userspace [ Upstream commit e97ff8b62d4690c69297f0f6de874f0564cc01a4 ] This fixes an inconsistency where io_nop() called req_set_fail() based on ret, but passed just nop->result to userspace. Originally, ret is a even copy of nop->result, but is set to an error when such happens subsequently. Now that's also passed to userspace. Fixes: a85f31052bce ("io_uring/nop: add support for testing registered files and buffers") Signed-off-by: Alexander A. Klimov Link: https://patch.msgid.link/20260520180045.538533-1-grandmaster@al2klimov.de Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- io_uring/nop.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io_uring/nop.c b/io_uring/nop.c index 3caf07878f8a..f5c9969e7f64 100644 --- a/io_uring/nop.c +++ b/io_uring/nop.c @@ -79,9 +79,9 @@ int io_nop(struct io_kiocb *req, unsigned int issue_flags) if (ret < 0) req_set_fail(req); if (nop->flags & IORING_NOP_CQE32) - io_req_set_res32(req, nop->result, 0, nop->extra1, nop->extra2); + io_req_set_res32(req, ret, 0, nop->extra1, nop->extra2); else - io_req_set_res(req, nop->result, 0); + io_req_set_res(req, ret, 0); if (nop->flags & IORING_NOP_TW) { req->io_task_work.func = io_req_task_complete; io_req_task_work_add(req); From a1a39f227c80cbf369767badc32cba2b225147d1 Mon Sep 17 00:00:00 2001 From: Junyi Liu Date: Mon, 18 May 2026 23:27:19 +0900 Subject: [PATCH 375/697] ksmbd: fix durable reconnect error path file lifetime [ Upstream commit 3515503322f4819277091839eed46b695096aca5 ] After a durable reconnect succeeds, ksmbd_reopen_durable_fd() republishes the same ksmbd_file into the session volatile-id table. If smb2_open() then takes a later error path, cleanup first calls ksmbd_fd_put(work, fp) and then unconditionally calls ksmbd_put_durable_fd(dh_info.fp). In this case fp and dh_info.fp are the same object. The first put drops the reconnect lookup reference, but the final durable put can run __ksmbd_close_fd(NULL, fp). Because the final close is not session-aware, it can free the file object without removing the volatile-id entry that was just published into the session table. Use the session-aware put for the final reconnect drop when the reconnect had already succeeded and the error path is cleaning up the republished file. Earlier reconnect failures, before fp is assigned to dh_info.fp, keep using the durable-only put path. Fixes: 1baff47b81f9 ("ksmbd: fix use-after-free in smb2_open during durable reconnect") Signed-off-by: Junyi Liu Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2pdu.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 756624b4e90e..da7b96707186 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -3812,8 +3812,19 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status); } - if (dh_info.reconnected) - ksmbd_put_durable_fd(dh_info.fp); + if (dh_info.reconnected) { + /* + * If reconnect succeeded, fp was republished in the + * session file table. On a later error, ksmbd_fd_put() + * above drops the session reference; drop the durable + * lookup reference through the same session-aware path so + * final close removes the volatile id before freeing fp. + */ + if (rc && fp == dh_info.fp) + ksmbd_fd_put(work, dh_info.fp); + else + ksmbd_put_durable_fd(dh_info.fp); + } kfree(name); kfree(lc); From 306ba9d0e5aa98465c2d5588f91994fbdf95c4d6 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Fri, 22 May 2026 15:05:07 +0800 Subject: [PATCH 376/697] LoongArch: kprobes: Fix handling of fatal unrecoverable recursions [ Upstream commit 1c856e158fd34ef2c4475a81c1dc386329989938 ] KPROBE_HIT_SS and KPROBE_REENTER are two types of fatal recursions that can not be safely recovered in kprobes. KPROBE_HIT_SS means that a kprobe is hit during single-stepping. At this point, the architecture-specific single-step context is already active. Nested single-stepping would corrupt the state, as the kprobe control block (kcb) and hardware registers cannot safely store multiple levels of stepping state. KPROBE_REENTER means that a third-level recursion occurs when a probe is hit while the system is already handling a nested probe (second- level). The kcb only provides a single slot (prev_kprobe) to backup the state. When a third probe is hit, there is no more space to save the state without corrupting the first-level backup. Kprobes work by replacing instructions with breakpoints. In order to execute the original instruction and continue, it must be moved to a temporary "single-step" slot. Since there is no backup space left to set up this slot safely, the CPU would be forced to return to the same original breakpoint address, triggering an endless loop. Currently, the code only prints a warning and returns. This leads to an infinite re-entry loop as the CPU repeatedly hits the same trap and a "stuck" CPU core because preemption was disabled at the start of the handler and never re-enabled in this early return path. Fix the logic by: 1. Merging KPROBE_HIT_SS and KPROBE_REENTER cases, as both represent fatal recursions that cannot be safely recovered. 2. Replacing WARN_ON_ONCE() with BUG() to terminate the system. This aligns LoongArch with other architectures (x86, arm64, riscv) and prevents stack overflow while providing diagnostic information. Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support") Signed-off-by: Tiezhu Yang Signed-off-by: Huacai Chen Signed-off-by: Sasha Levin --- arch/loongarch/kernel/kprobes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/loongarch/kernel/kprobes.c b/arch/loongarch/kernel/kprobes.c index 04b5b05715cd..1985ed30dd16 100644 --- a/arch/loongarch/kernel/kprobes.c +++ b/arch/loongarch/kernel/kprobes.c @@ -186,16 +186,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) { switch (kcb->kprobe_status) { - case KPROBE_HIT_SS: case KPROBE_HIT_SSDONE: case KPROBE_HIT_ACTIVE: kprobes_inc_nmissed_count(p); setup_singlestep(p, regs, kcb, 1); break; + case KPROBE_HIT_SS: case KPROBE_REENTER: pr_warn("Failed to recover from reentered kprobes.\n"); dump_kprobe(p); - WARN_ON_ONCE(1); + BUG(); break; default: WARN_ON(1); From 239172639075ef745ca233386086ba4b2b5077f8 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 18 May 2026 15:17:14 -0700 Subject: [PATCH 377/697] drm/msm: Restore second parameter name in purge() and evict() [ Upstream commit 53676e4d44d6b38c8a0d9bff331f170ae2e41bbe ] After commit 3392291fc509 ("drm/msm: Fix shrinker deadlock"), all supported versions of clang warn (or error with CONFIG_WERROR=y): drivers/gpu/drm/msm/msm_gem_shrinker.c:105:58: error: omitting the parameter name in a function definition is a C23 extension [-Werror,-Wc23-extensions] 105 | purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) | ^ drivers/gpu/drm/msm/msm_gem_shrinker.c:117:58: error: omitting the parameter name in a function definition is a C23 extension [-Werror,-Wc23-extensions] 117 | evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) | ^ 2 errors generated. With older but supported versions of GCC, this is an unconditional hard error: drivers/gpu/drm/msm/msm_gem_shrinker.c: In function 'purge': drivers/gpu/drm/msm/msm_gem_shrinker.c:105:35: error: parameter name omitted purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) ^~~~~~~~~~~~~~~~~~~~~~~ drivers/gpu/drm/msm/msm_gem_shrinker.c: In function 'evict': drivers/gpu/drm/msm/msm_gem_shrinker.c:117:35: error: parameter name omitted evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) ^~~~~~~~~~~~~~~~~~~~~~~ Restore the parameter name to clear up the warnings, renaming it "unused" to make it clear it is only needed to satisfy the prototype of drm_gem_lru_scan(). Cc: stable@vger.kernel.org Fixes: 3392291fc509 ("drm/msm: Fix shrinker deadlock") Signed-off-by: Nathan Chancellor Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin --- drivers/gpu/drm/msm/msm_gem_shrinker.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/msm/msm_gem_shrinker.c b/drivers/gpu/drm/msm/msm_gem_shrinker.c index 6e39e4e578bb..8f118b5185a1 100644 --- a/drivers/gpu/drm/msm/msm_gem_shrinker.c +++ b/drivers/gpu/drm/msm/msm_gem_shrinker.c @@ -102,7 +102,7 @@ with_vm_locks(void (*fn)(struct drm_gem_object *obj), } static bool -purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) +purge(struct drm_gem_object *obj, struct ww_acquire_ctx *unused) { if (!is_purgeable(to_msm_bo(obj))) return false; @@ -114,7 +114,7 @@ purge(struct drm_gem_object *obj, struct ww_acquire_ctx *) } static bool -evict(struct drm_gem_object *obj, struct ww_acquire_ctx *) +evict(struct drm_gem_object *obj, struct ww_acquire_ctx *unused) { if (is_unevictable(to_msm_bo(obj))) return false; From 50bb3435a5e627bfbdc52eb4536f49f88b3486b8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 28 May 2026 11:45:41 -0700 Subject: [PATCH 378/697] security/keys: fix missed RCU read section on lookup commit 43a1e3744548e6fd85873e6fb43e293eb4010694 upstream. Nicholas Carlini reports that the keyring code calls assoc_array_find() in find_key_to_update() without holding the RCU read lock, while the assoc_array_gc() code really is designed around removing the node from the tree and then freeing it after an RCU grace-period. The regular key handling doesn't see this because holding the keyring semaphore hides any lifetime issues, but the persistent key handling uses a different model. Instead of extending the keyring locking, just do the simple RCU locking that the assoc_array was designed for. Reported-by: Nicholas Carlini Cc: David Howells Cc: Jarkko Sakkinen Cc: Paul Moore Cc: James Morris James Morris Cc: Serge E. Hallyn Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- security/keys/keyring.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/keys/keyring.c b/security/keys/keyring.c index f331725d5a37..df2580072cfe 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1109,6 +1109,7 @@ key_ref_t find_key_to_update(key_ref_t keyring_ref, kenter("{%d},{%s,%s}", keyring->serial, index_key->type->name, index_key->description); + guard(rcu)(); object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops, index_key); From 18ad16ce4a6b2714583fd1e1044c6ea8e53b3519 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 1 Jun 2026 17:51:08 +0200 Subject: [PATCH 379/697] Linux 6.18.34 Link: https://lore.kernel.org/r/20260528194638.371537336@linuxfoundation.org Tested-by: Ron Economos Tested-by: Miguel Ojeda Tested-by: Brett A C Sheffield Tested-by: Pavel Machek (CIP) Tested-by: Peter Schneider Tested-by: Wentao Guan Tested-by: Florian Fainelli Tested-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dc63a98489a5..5087bd6183dd 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 33 +SUBLEVEL = 34 EXTRAVERSION = NAME = Baby Opossum Posse From 0ca809ea8e0355299266c46e5f1755040aa8dcf3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Apr 2026 18:00:27 +0200 Subject: [PATCH 380/697] Input: usbtouchscreen - clamp NEXIO data_len/x_len to URB buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2905281cbda52ec9df540113b35b835feb5fafd3 upstream. nexio_read_data() pulls data_len and x_len from a packed __be16 header in the device's interrupt packet and then walks packet->data[0..x_len) and packet->data[x_len..data_len) comparing each byte against a threshold. Both fields are 16-bit on the wire (max 65535). The existing adjustments shave at most 0x100 / 0x80 off, so the loop bound can still reach roughly 0xfeff. The URB transfer buffer for NEXIO is rept_size (1024) bytes from usb_alloc_coherent(), with the first 7 occupied by the packed header — so packet->data[] has 1017 valid bytes. read_data() callbacks are not given urb->actual_length, and nothing else bounds the walk. A device that lies about its length can get a ~64 KiB out-of-bounds read past the coherent DMA allocation. The first index whose byte exceeds NEXIO_THRESHOLD lands in begin_x / begin_y and from there into the reported touch coordinates, so adjacent kernel memory contents leak to userspace as ABS_X / ABS_Y events. Far enough out, the read can also hit an unmapped page and fault. Fix this all by clamping data_len to the buffer's data[] capacity and x_len to data_len. Cc: Dmitry Torokhov Fixes: 5197424cdccc ("Input: usbtouchscreen - add NEXIO (or iNexio) support") Cc: stable Assisted-by: gkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026042026-chlorine-epidermis-fd6d@gregkh Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/usbtouchscreen.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 7567efabe014..cc4a0d3b8a80 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -1070,6 +1070,11 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) if (x_len > 0xff) x_len -= 0x80; + if (data_len > usbtouch->data_size - sizeof(*packet)) + data_len = usbtouch->data_size - sizeof(*packet); + if (x_len > data_len) + x_len = data_len; + /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); if (ret) From 41845bc5bb64f3d615abe575ad655b5e7f193634 Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Wed, 8 Apr 2026 17:24:36 +0200 Subject: [PATCH 381/697] net/sched: cls_fw: fix NULL dereference of "old" filters before change() [ Upstream commit 65782b2db7321d5f97c16718c4c7f6c7205a56be ] Like pointed out by Sashiko [1], since commit ed76f5edccc9 ("net: sched: protect filter_chain list with filter_chain_lock mutex") TC filters are added to a shared block and published to datapath before their ->change() function is called. This is a problem for cls_fw: an invalid filter created with the "old" method can still classify some packets before it is destroyed by the validation logic added by Xiang. Therefore, insisting with repeated runs of the following script: # ip link add dev crash0 type dummy # ip link set dev crash0 up # mausezahn crash0 -c 100000 -P 10 \ > -A 4.3.2.1 -B 1.2.3.4 -t udp "dp=1234" -q & # sleep 1 # tc qdisc add dev crash0 egress_block 1 clsact # tc filter add block 1 protocol ip prio 1 matchall \ > action skbedit mark 65536 continue # tc filter add block 1 protocol ip prio 2 fw # ip link del dev crash0 can still make fw_classify() hit the WARN_ON() in [2]: WARNING: ./include/net/pkt_cls.h:88 at fw_classify+0x244/0x250 [cls_fw], CPU#18: mausezahn/1399 Modules linked in: cls_fw(E) act_skbedit(E) CPU: 18 UID: 0 PID: 1399 Comm: mausezahn Tainted: G E 7.0.0-rc6-virtme #17 PREEMPT(full) Tainted: [E]=UNSIGNED_MODULE Hardware name: Red Hat KVM, BIOS 1.16.3-2.el9 04/01/2014 RIP: 0010:fw_classify+0x244/0x250 [cls_fw] Code: 5c 49 c7 45 00 00 00 00 00 41 5d 41 5e 41 5f 5d c3 cc cc cc cc 5b b8 ff ff ff ff 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc 90 <0f> 0b 90 eb a0 0f 1f 80 00 00 00 00 90 90 90 90 90 90 90 90 90 90 RSP: 0018:ffffd1b7026bf8a8 EFLAGS: 00010202 RAX: ffff8c5ac9c60800 RBX: ffff8c5ac99322c0 RCX: 0000000000000004 RDX: 0000000000000001 RSI: ffff8c5b74d7a000 RDI: ffff8c5ac8284f40 RBP: ffffd1b7026bf8d0 R08: 0000000000000000 R09: ffffd1b7026bf9b0 R10: 00000000ffffffff R11: 0000000000000000 R12: 0000000000010000 R13: ffffd1b7026bf930 R14: ffff8c5ac8284f40 R15: 0000000000000000 FS: 00007fca40c37740(0000) GS:ffff8c5b74d7a000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fca40e822a0 CR3: 0000000005ca0001 CR4: 0000000000172ef0 Call Trace: tcf_classify+0x17d/0x5c0 tc_run+0x9d/0x150 __dev_queue_xmit+0x2ab/0x14d0 ip_finish_output2+0x340/0x8f0 ip_output+0xa4/0x250 raw_sendmsg+0x147d/0x14b0 __sys_sendto+0x1cc/0x1f0 __x64_sys_sendto+0x24/0x30 do_syscall_64+0x126/0xf80 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7fca40e822ba Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89 RSP: 002b:00007ffc248a42c8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c RAX: ffffffffffffffda RBX: 000055ef233289d0 RCX: 00007fca40e822ba RDX: 000000000000001e RSI: 000055ef23328c30 RDI: 0000000000000003 RBP: 000055ef233289d0 R08: 00007ffc248a42d0 R09: 0000000000000010 R10: 0000000000000000 R11: 0000000000000246 R12: 000000000000001e R13: 00000000000186a0 R14: 0000000000000000 R15: 00007fca41043000 irq event stamp: 1045778 hardirqs last enabled at (1045784): [] __up_console_sem+0x52/0x60 hardirqs last disabled at (1045789): [] __up_console_sem+0x37/0x60 softirqs last enabled at (1045426): [] __alloc_skb+0x207/0x260 softirqs last disabled at (1045434): [] __dev_queue_xmit+0x78/0x14d0 Then, because of the value in the packet's mark, dereference on 'q->handle' with NULL 'q' occurs: BUG: kernel NULL pointer dereference, address: 0000000000000038 [...] RIP: 0010:fw_classify+0x1fe/0x250 [cls_fw] [...] Skip "old-style" classification on shared blocks, so that the NULL dereference is fixed and WARN_ON() is not hit anymore in the short lifetime of invalid cls_fw "old-style" filters. [1] https://sashiko.dev/#/patchset/20260331050217.504278-1-xmei5%40asu.edu [2] https://elixir.bootlin.com/linux/v7.0-rc6/source/include/net/pkt_cls.h#L86 Fixes: faeea8bbf6e9 ("net/sched: cls_fw: fix NULL pointer dereference on shared blocks") Fixes: ed76f5edccc9 ("net: sched: protect filter_chain list with filter_chain_lock mutex") Acked-by: Jamal Hadi Salim Signed-off-by: Davide Caratti Link: https://patch.msgid.link/e39cbd3103a337f1e515d186fe697b4459d24757.1775661704.git.dcaratti@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/cls_fw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 83a7372ea15c..fd9c6c2815a1 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -74,9 +74,13 @@ TC_INDIRECT_SCOPE int fw_classify(struct sk_buff *skb, } } } else { - struct Qdisc *q = tcf_block_q(tp->chain->block); + struct Qdisc *q; /* Old method: classify the packet using its skb mark. */ + if (tcf_block_shared(tp->chain->block)) + return -1; + + q = tcf_block_q(tp->chain->block); if (id && (TC_H_MAJ(id) == 0 || !(TC_H_MAJ(id ^ q->handle)))) { res->classid = id; From 54ed418de62a148a655262da682a050fa05f7924 Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Tue, 2 Jun 2026 15:34:28 +0800 Subject: [PATCH 382/697] net: mctp: ensure our nlmsg responses are initialised [ Upstream commit a6a9bc544b675d8b5180f2718ec985ad267b5cbf ] Syed Faraz Abrar (@farazsth98) from Zellic, and Pumpkin (@u1f383) from DEVCORE Research Team working with Trend Micro Zero Day Initiative report that a RTM_GETNEIGH will return uninitalised data in the pad bytes of the ndmsg data. Ensure we're initialising the netlink data to zero, in the link, addr and neigh response messages. Fixes: 831119f88781 ("mctp: Add neighbour netlink interface") Fixes: 06d2f4c583a7 ("mctp: Add netlink route management") Fixes: 583be982d934 ("mctp: Add device handling and netlink interface") Signed-off-by: Jeremy Kerr Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260209-dev-mctp-nlmsg-v1-1-f1e30c346a43@codeconstruct.com.au Signed-off-by: Jakub Kicinski Signed-off-by: Li hongliang <1468888505@139.com> Signed-off-by: Sasha Levin --- net/mctp/device.c | 1 + net/mctp/neigh.c | 1 + net/mctp/route.c | 1 + 3 files changed, 3 insertions(+) diff --git a/net/mctp/device.c b/net/mctp/device.c index 4d404edd7446..04c5570bacff 100644 --- a/net/mctp/device.c +++ b/net/mctp/device.c @@ -70,6 +70,7 @@ static int mctp_fill_addrinfo(struct sk_buff *skb, return -EMSGSIZE; hdr = nlmsg_data(nlh); + memset(hdr, 0, sizeof(*hdr)); hdr->ifa_family = AF_MCTP; hdr->ifa_prefixlen = 0; hdr->ifa_flags = 0; diff --git a/net/mctp/neigh.c b/net/mctp/neigh.c index 05b899f22d90..fc85f0e69301 100644 --- a/net/mctp/neigh.c +++ b/net/mctp/neigh.c @@ -218,6 +218,7 @@ static int mctp_fill_neigh(struct sk_buff *skb, u32 portid, u32 seq, int event, return -EMSGSIZE; hdr = nlmsg_data(nlh); + memset(hdr, 0, sizeof(*hdr)); hdr->ndm_family = AF_MCTP; hdr->ndm_ifindex = dev->ifindex; hdr->ndm_state = 0; // TODO other state bits? diff --git a/net/mctp/route.c b/net/mctp/route.c index d4fdaac8037a..eb817f1eb5c8 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -1650,6 +1650,7 @@ static int mctp_fill_rtinfo(struct sk_buff *skb, struct mctp_route *rt, return -EMSGSIZE; hdr = nlmsg_data(nlh); + memset(hdr, 0, sizeof(*hdr)); hdr->rtm_family = AF_MCTP; /* we use the _len fields as a number of EIDs, rather than From 91cc13978ab0bc6f669139f53e7e613a860d10e0 Mon Sep 17 00:00:00 2001 From: Usama Arif Date: Thu, 21 May 2026 03:29:26 -0700 Subject: [PATCH 383/697] xfrm: move policy_bydst RCU sync from per-netns .exit to .pre_exit [ Upstream commit 3e52417318473782012b236d0325bf7d2266a597 ] The struct pernet_operations docstring in include/net/net_namespace.h explicitly warns against blocking RCU primitives in .exit handlers: Exit methods using blocking RCU primitives, such as synchronize_rcu(), should be implemented via exit_batch. [...] Please, avoid synchronize_rcu() at all, where it's possible. Note that a combination of pre_exit() and exit() can be used, since a synchronize_rcu() is guaranteed between the calls. xfrm_policy_fini() violates this: it calls synchronize_rcu() before freeing the policy_bydst hash tables (so no RCU reader is mid- traversal at free time), but runs from xfrm_net_ops.exit -- once per namespace -- so a cleanup_net() of N namespaces pays N full RCU grace periods serially. Use the documented pre_exit/exit split. Move the policy flush (and the workqueue drains it depends on) into a new .pre_exit handler; xfrm_policy_fini() then runs in .exit and frees the hash tables after the synchronize_rcu_expedited() that cleanup_net() guarantees between the two phases. Providing O(1) RCU grace periods per batch instead of O(N). Observed on Linux 6.18 with a workload doing unshare(CLONE_NEWNET) at ~13/sec sustained: cleanup_net() and the netns_wq rescuer kthread both stuck in xfrm_policy_fini()'s synchronize_rcu(), >300k struct net accumulated in the cleanup queue, Percpu in /proc/meminfo climbed to 130+ GB on 256-CPU hosts, and memcg OOMs followed. setup_net and __put_net counts were balanced, ruling out a refcount leak. Fixes: 069daad4f2ae ("xfrm: Wait for RCU readers during policy netns exit") Signed-off-by: Usama Arif Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_policy.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 29c94ee0ceb2..96e9262c4b48 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4276,21 +4276,21 @@ static int __net_init xfrm_policy_init(struct net *net) return -ENOMEM; } -static void xfrm_policy_fini(struct net *net) +static void __net_exit xfrm_net_pre_exit(struct net *net) { - struct xfrm_pol_inexact_bin *b, *t; - unsigned int sz; - int dir; - disable_work_sync(&net->xfrm.policy_hthresh.work); - flush_work(&net->xfrm.policy_hash_work); #ifdef CONFIG_XFRM_SUB_POLICY xfrm_policy_flush(net, XFRM_POLICY_TYPE_SUB, false); #endif xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, false); +} - synchronize_rcu(); +static void xfrm_policy_fini(struct net *net) +{ + struct xfrm_pol_inexact_bin *b, *t; + unsigned int sz; + int dir; WARN_ON(!list_empty(&net->xfrm.policy_all)); @@ -4368,6 +4368,7 @@ static void __net_exit xfrm_net_exit(struct net *net) static struct pernet_operations __net_initdata xfrm_net_ops = { .init = xfrm_net_init, + .pre_exit = xfrm_net_pre_exit, .exit = xfrm_net_exit, }; From dbc560858da8b77dd9e4ef0cd93d421e0e4d7e0e Mon Sep 17 00:00:00 2001 From: Victor Nogueria Date: Thu, 30 Apr 2026 11:29:56 -0400 Subject: [PATCH 384/697] net/sched: sch_sfb: Replace direct dequeue call with peek and qdisc_dequeue_peeked [ Upstream commit 1b9bc71153b01dbde8045b9edede4240f4f5520e ] When sfb has children (eg qfq qdisc) whose peek() callback is qdisc_peek_dequeued(), we could get a kernel panic. When the parent of such qdiscs (eg illustrated in patch #3 as tbf) wants to retrieve an skb from its child (sfb in this case), it will do the following: 1a. do a peek() - and when sensing there's an skb the child can offer, then - the child in this case(sfb) calls its child's (qfq) peek. qfq does the right thing and will return the gso_skb queue packet. Note: if there wasnt a gso_skb entry then qfq will store it there. 1b. invoke a dequeue() on the child (sfb). And herein lies the problem. - sfb will call the child's dequeue() which will essentially just try to grab something of qfq's queue. [ 127.594489][ T453] KASAN: null-ptr-deref in range [0x0000000000000048-0x000000000000004f] [ 127.594741][ T453] CPU: 2 UID: 0 PID: 453 Comm: ping Not tainted 7.1.0-rc1-00035-gac961974495b-dirty #793 PREEMPT(full) [ 127.595059][ T453] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 [ 127.595254][ T453] RIP: 0010:qfq_dequeue+0x35c/0x1650 [sch_qfq] [ 127.595461][ T453] Code: 00 fc ff df 80 3c 02 00 0f 85 17 0e 00 00 4c 8d 73 48 48 89 9d b8 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 89 f2 48 c1 ea 03 <80> 3c 02 00 0f 85 76 0c 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b [ 127.596081][ T453] RSP: 0018:ffff88810e5af440 EFLAGS: 00010216 [ 127.596337][ T453] RAX: dffffc0000000000 RBX: 0000000000000000 RCX: dffffc0000000000 [ 127.596623][ T453] RDX: 0000000000000009 RSI: 0000001880000000 RDI: ffff888104fd82b0 [ 127.596917][ T453] RBP: ffff888104fd8000 R08: ffff888104fd8280 R09: 1ffff110211893a3 [ 127.597165][ T453] R10: 1ffff110211893a6 R11: 1ffff110211893a7 R12: 0000001880000000 [ 127.597404][ T453] R13: ffff888104fd82b8 R14: 0000000000000048 R15: 0000000040000000 [ 127.597644][ T453] FS: 00007fc380cbfc40(0000) GS:ffff88816f2a8000(0000) knlGS:0000000000000000 [ 127.597956][ T453] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 127.598160][ T453] CR2: 00005610aa9890a8 CR3: 000000010369e000 CR4: 0000000000750ef0 [ 127.598390][ T453] PKRU: 55555554 [ 127.598509][ T453] Call Trace: [ 127.598629][ T453] [ 127.598718][ T453] ? mark_held_locks+0x40/0x70 [ 127.598890][ T453] ? srso_alias_return_thunk+0x5/0xfbef5 [ 127.599053][ T453] sfb_dequeue+0x88/0x4d0 [ 127.599174][ T453] ? ktime_get+0x137/0x230 [ 127.599328][ T453] ? srso_alias_return_thunk+0x5/0xfbef5 [ 127.599480][ T453] ? qdisc_peek_dequeued+0x7b/0x350 [sch_qfq] [ 127.599670][ T453] ? srso_alias_return_thunk+0x5/0xfbef5 [ 127.599831][ T453] tbf_dequeue+0x6b1/0x1098 [sch_tbf] [ 127.599988][ T453] __qdisc_run+0x169/0x1900 The right thing to do in #1b is to grab the skb off gso_skb queue. This patchset fixes that issue by changing #1b to use qdisc_dequeue_peeked() method instead. Fixes: e13e02a3c68d ("net_sched: SFB flow scheduler") Signed-off-by: Victor Nogueria Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260430152957.194015-3-jhs@mojatatu.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sched/sch_sfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 00286c930b8d..14ac88977847 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -441,7 +441,7 @@ static struct sk_buff *sfb_dequeue(struct Qdisc *sch) struct Qdisc *child = q->qdisc; struct sk_buff *skb; - skb = child->dequeue(q->qdisc); + skb = qdisc_dequeue_peeked(child); if (skb) { qdisc_bstats_update(sch, skb); From 8b733ee4aecd03930e5c02f4365044d93b51f117 Mon Sep 17 00:00:00 2001 From: Mingzhe Zou Date: Fri, 3 Apr 2026 12:21:35 +0800 Subject: [PATCH 385/697] bcache: fix uninitialized closure object [ Upstream commit 20a8e451ec1c7e99060b1bbaaad03ce88c39ddb8 ] In the previous patch ("bcache: fix cached_dev.sb_bio use-after-free and crash"), we adopted a simple modification suggestion from AI to fix the use-after-free. But in actual testing, we found an extreme case where the device is stopped before calling bch_write_bdev_super(). At this point, struct closure sb_write has not been initialized yet. For this patch, we ensure that sb_bio has been completed via sb_write_mutex. Signed-off-by: Mingzhe Zou Signed-off-by: Coly Li Link: https://patch.msgid.link/20260403042135.2221247-1-colyli@fnnas.com Fixes: fec114a98b87 ("bcache: fix cached_dev.sb_bio use-after-free and crash") Signed-off-by: Jens Axboe Signed-off-by: Sasha Levin --- drivers/md/bcache/super.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 2f06945533d6..d4ebd13a59f8 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1378,7 +1378,8 @@ static CLOSURE_CALLBACK(cached_dev_free) * The sb_bio is embedded in struct cached_dev, so we must * ensure no I/O is in progress. */ - closure_sync(&dc->sb_write); + down(&dc->sb_write_mutex); + up(&dc->sb_write_mutex); if (dc->sb_disk) folio_put(virt_to_folio(dc->sb_disk)); From 912ebc49d4406a17fe73e5671d674fbc2f6b2634 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 29 Apr 2026 13:40:41 +0000 Subject: [PATCH 386/697] nfc: llcp: Fix use-after-free in llcp_sock_release() [ Upstream commit f4268b466190dae95a7585f69b4f1f8ad097632c ] llcp_sock_release() unconditionally unlinks the socket from the local sockets list. However, if the socket is still in connecting state, it is on the connecting list. Fix this by checking the socket state and unlinking from the correct list. Fixes: b4011239a08e ("NFC: llcp: Fix non blocking sockets connections") Signed-off-by: Lee Jones Link: https://patch.msgid.link/20260429134115.3558604-1-lee@kernel.org Signed-off-by: David Heidelberg Signed-off-by: Sasha Levin --- net/nfc/llcp_sock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c index 57a2f97004e1..915929cd724f 100644 --- a/net/nfc/llcp_sock.c +++ b/net/nfc/llcp_sock.c @@ -633,6 +633,8 @@ static int llcp_sock_release(struct socket *sock) if (sock->type == SOCK_RAW) nfc_llcp_sock_unlink(&local->raw_sockets, sk); + else if (sk->sk_state == LLCP_CONNECTING) + nfc_llcp_sock_unlink(&local->connecting_sockets, sk); else nfc_llcp_sock_unlink(&local->sockets, sk); From 650bdd8fdfab64a09ee474150313dbc48c374795 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 29 Apr 2026 13:40:42 +0000 Subject: [PATCH 387/697] nfc: llcp: Fix use-after-free race in nfc_llcp_recv_cc() [ Upstream commit b493ea2765cc17cb8aa7e7544a4b6dcb05b6ed77 ] A race condition exists in the NFC LLCP connection state machine where the connection acceptance packet (CC) can be processed concurrently with socket release. This can lead to a use-after-free of the socket object. When nfc_llcp_recv_cc() moves the socket from the connecting_sockets list to the sockets list, it does so without holding the socket lock. If llcp_sock_release() is executing concurrently, it might have already unlinked the socket and dropped its references, which can result in nfc_llcp_recv_cc() linking a freed socket into the live list. Fix this by holding lock_sock() during the state transition and list movement in nfc_llcp_recv_cc(). After acquiring the lock, check if the socket is still hashed to ensure it hasn't already been unlinked and marked for destruction by the release path. This aligns the locking pattern with recv_hdlc() and recv_disc(). Fixes: a69f32af86e3 ("NFC: Socket linked list") Signed-off-by: Lee Jones Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260429134115.3558604-2-lee@kernel.org Signed-off-by: David Heidelberg Signed-off-by: Sasha Levin --- net/nfc/llcp_core.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index da8d3add0018..c83a00e42985 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -1218,6 +1218,15 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local, sk = &llcp_sock->sk; + lock_sock(sk); + + /* Check if socket was destroyed whilst waiting for the lock */ + if (!sk_hashed(sk)) { + release_sock(sk); + nfc_llcp_sock_put(llcp_sock); + return; + } + /* Unlink from connecting and link to the client array */ nfc_llcp_sock_unlink(&local->connecting_sockets, sk); nfc_llcp_sock_link(&local->sockets, sk); @@ -1229,6 +1238,8 @@ static void nfc_llcp_recv_cc(struct nfc_llcp_local *local, sk->sk_state = LLCP_CONNECTED; sk->sk_state_change(sk); + release_sock(sk); + nfc_llcp_sock_put(llcp_sock); } From 82ac903e0b519849647657b8c48d21237ada06a2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 13 May 2026 10:49:14 -0600 Subject: [PATCH 388/697] xfrm: Check for underflow in xfrm_state_mtu [ Upstream commit 742b04d0550b0ec89dcbc99537ec88653bd1ad90 ] Leo Lin reported OOB write issue in esp component: xfrm_state_mtu() returns u32 but performs its arithmetic in unsigned modulo-2^32 space using an attacker-influenced "header_len + authsize + net_adj" subtracted from a small "mtu" argument. A nobody user can install an IPv4 ESP tunnel SA with a large authentication key (XFRMA_ALG_AUTH_TRUNC, e.g. hmac(sha512), 64-byte key, 64-byte trunc), configure a small interface MTU (68 bytes), and set XFRMA_TFCPAD to a large value. When a single UDP datagram is then sent through the tunnel, xfrm_state_mtu() underflows to a near-2^32 value, and esp_output() consumes it as a signed int via: padto = min(x->tfcpad, xfrm_state_mtu(x, mtu_cached)) esp.tfclen = padto - skb->len (assigned to int) esp.tfclen ends up negative (e.g. -207). It is sign-extended to size_t when passed to memset() inside esp_output_fill_trailer(), producing a ~16 EB write of zeroes at skb_tail_pointer(skb). KASAN logs it as "Write of size 18446744073709551537 at addr ffff888...". Check for underflow and return 1. This causes the sendmsg attempt to fail with ENETUNREACH. Fixes: c5c252389374 ("[XFRM]: Optimize MTU calculation") Reported-by: Leo Lin Assisted-by: Codex:26.506.31004 Signed-off-by: David Ahern Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin --- net/xfrm/xfrm_state.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index f6ba58f18ac1..1b81e92e3eee 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -3113,10 +3113,14 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) const struct xfrm_type *type = READ_ONCE(x->type); struct crypto_aead *aead; u32 blksize, net_adj = 0; + u32 overhead, payload_mtu; if (x->km.state != XFRM_STATE_VALID || - !type || type->proto != IPPROTO_ESP) + !type || type->proto != IPPROTO_ESP) { + if (mtu <= x->props.header_len) + return 1; return mtu - x->props.header_len; + } aead = x->data; blksize = ALIGN(crypto_aead_blocksize(aead), 4); @@ -3139,8 +3143,17 @@ u32 xfrm_state_mtu(struct xfrm_state *x, int mtu) break; } - return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - - net_adj) & ~(blksize - 1)) + net_adj - 2; + overhead = x->props.header_len + crypto_aead_authsize(aead) + net_adj; + if (mtu <= overhead) + return 1; + + payload_mtu = mtu - overhead; + payload_mtu &= ~(blksize - 1); + if (payload_mtu <= 2) + return 1; + + return payload_mtu + net_adj - 2; + } EXPORT_SYMBOL_GPL(xfrm_state_mtu); From b4702049417f12e07e87f7eda80121c236950967 Mon Sep 17 00:00:00 2001 From: Carl Lee Date: Sat, 16 May 2026 19:55:18 +0800 Subject: [PATCH 389/697] nfc: nxp-nci: i2c: use rising-edge IRQ on ACPI systems [ Upstream commit f23bf992d65a42007c517b060ca35cebdea3525a ] Some ACPI-based platforms report incorrect IRQ trigger types (e.g. IRQF_TRIGGER_HIGH), which can lead to interrupt storms. Use the historically working rising-edge trigger on ACPI systems to avoid this regression. Device Tree-based systems continue to use the firmware-provided trigger type. Fixes: 57be33f85e36 ("nfc: nxp-nci: remove interrupt trigger type") Signed-off-by: Carl Lee Tested-by: Bartosz Golaszewski Reviewed-by: Bartosz Golaszewski Reviewed-by: Mark Pearson Tested-by: Mark Pearson Tested-by: Luca Stefani Link: https://patch.msgid.link/20260516-nfc-nxp-nci-i2c-restore-irq-trigger-fallback-v3-1-37ba4b6e9086@amd.com Signed-off-by: David Heidelberg Signed-off-by: Sasha Levin --- drivers/nfc/nxp-nci/i2c.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c index b3d34433bd14..a6c08175d9dd 100644 --- a/drivers/nfc/nxp-nci/i2c.c +++ b/drivers/nfc/nxp-nci/i2c.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -267,6 +268,7 @@ static int nxp_nci_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct nxp_nci_i2c_phy *phy; + unsigned long irqflags; int r; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -303,9 +305,26 @@ static int nxp_nci_i2c_probe(struct i2c_client *client) if (r < 0) return r; + /* + * ACPI platforms may report incorrect IRQ trigger types + * (e.g. level-high), which can lead to interrupt storms. + * + * Use the historically stable rising-edge trigger for ACPI devices. + * + * On non-ACPI systems (e.g. Device Tree), prefer the firmware- + * provided trigger type, falling back to rising-edge if not set. + */ + if (ACPI_COMPANION(dev)) { + irqflags = IRQF_TRIGGER_RISING; + } else { + irqflags = irq_get_trigger_type(client->irq); + if (!irqflags) + irqflags = IRQF_TRIGGER_RISING; + } + r = request_threaded_irq(client->irq, NULL, nxp_nci_i2c_irq_thread_fn, - IRQF_ONESHOT, + irqflags | IRQF_ONESHOT, NXP_NCI_I2C_DRIVER_NAME, phy); if (r < 0) nfc_err(&client->dev, "Unable to register IRQ handler\n"); From bebc7dc0fb4b03148272d68ae07985a242439aed Mon Sep 17 00:00:00 2001 From: Hongtao Lee Date: Wed, 20 May 2026 11:01:26 +0800 Subject: [PATCH 390/697] tools/bootconfig: Fix buf leaks in apply_xbc [ Upstream commit f42d01aadcedd7bbf4f9a466cabe25c1781dedad ] If data calloc failed, free the buf before return. Link: https://lore.kernel.org/all/20260520030126.147782-1-lihongtao@kylinos.cn/ Fixes: 950313ebf79c ("tools: bootconfig: Add bootconfig command") Signed-off-by: Hongtao Lee Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Sasha Levin --- tools/bootconfig/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index 643f707b8f1d..ddabde20585f 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -390,8 +390,10 @@ static int apply_xbc(const char *path, const char *xbc_path) /* Backup the bootconfig data */ data = calloc(size + BOOTCONFIG_ALIGN + BOOTCONFIG_FOOTER_SIZE, 1); - if (!data) + if (!data) { + free(buf); return -ENOMEM; + } memcpy(data, buf, size); /* Check the data format */ From e1b8a53834dc951b0e024b45d3edb4e390d70e55 Mon Sep 17 00:00:00 2001 From: Liu Kai Date: Thu, 7 May 2026 16:32:04 +0800 Subject: [PATCH 391/697] HID: remove duplicate hid_warn_ratelimited definition [ Upstream commit dd2147375a8fe7c5bc3f1f1b1d3a9567c26faefa ] The hid_warn_ratelimited macro is defined twice in include/linux/hid.h: - first one added by commit 4051ead99888 ("HID: rate-limit hid_warn to prevent log flooding") - second one added by commit 1d64624243af ("HID: core: Add printk_ratelimited variants to hid_warn() etc")). The second definition is correctly grouped with other ratelimited macros. Remove the duplicate definition. Fixes: 1d64624243af ("HID: core: Add printk_ratelimited variants to hid_warn() etc") Signed-off-by: Liu Kai [bentiss: edited commit message] Signed-off-by: Benjamin Tissoires Signed-off-by: Sasha Levin --- include/linux/hid.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 204ada8d12e5..29561887bea8 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1276,8 +1276,6 @@ void hid_quirks_exit(__u16 bus); dev_notice(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_warn(hid, fmt, ...) \ dev_warn(&(hid)->dev, fmt, ##__VA_ARGS__) -#define hid_warn_ratelimited(hid, fmt, ...) \ - dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_info(hid, fmt, ...) \ dev_info(&(hid)->dev, fmt, ##__VA_ARGS__) #define hid_dbg(hid, fmt, ...) \ From 45564a16a24f2c2573fedb7186a9e4adbacb2ad4 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Thu, 7 May 2026 10:48:54 +0200 Subject: [PATCH 392/697] kunit: fix use-after-free in debugfs when using kunit.filter [ Upstream commit fb6988b83b4cafe8db63999c1ddff1b7c66d2ff5 ] When the kernel is booted with a kunit filter (e.g., kunit.filter="speed!=slow"), the kunit executor dynamically allocates copies of the filtered test suites using kmalloc/kmemdup. During the initial boot execution, kunit_debugfs_create_suite() creates debugfs files (such as /sys/kernel/debug/kunit//run) and permanently stores a pointer to the dynamically allocated suite in the inode's i_private field. Previously, the executor freed this dynamically allocated suite_set immediately after executing the boot-time tests. Because the debugfs nodes were not destroyed, any subsequent interaction with the debugfs `run` file from userspace triggered a use-after-free (UAF). On systems with architectural capabilities, like CHERI RISC-V, this resulted in an immediate fatal hardware exception due to the invalidation of the capability tags on the reclaimed memory. On other architectures, it resulted in silent memory corruption. Fix this UAF by properly coupling the lifetime of the filtered suite memory allocation to the lifetime of the kunit subsystem and its associated VFS nodes. Ownership of the boot-time suite_set is now transferred to a global tracker ('kunit_boot_suites'), and the memory is cleanly released in kunit_exit() during module teardown. Link: https://lore.kernel.org/r/20260507084854.233984-1-florian.schmaus@codasip.com Fixes: e2219db280e3 ("kunit: add debugfs /sys/kernel/debug/kunit//results display") Signed-off-by: Florian Schmaus Reviewed-by: Martin Kaiser Reviewed-by: David Gow Signed-off-by: Shuah Khan Signed-off-by: Sasha Levin --- include/kunit/test.h | 1 + lib/kunit/executor.c | 19 ++++++++++++++++--- lib/kunit/test.c | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/include/kunit/test.h b/include/kunit/test.h index 5ec5182b5e57..aedffe2f2d49 100644 --- a/include/kunit/test.h +++ b/include/kunit/test.h @@ -613,6 +613,7 @@ unsigned long kunit_vm_mmap(struct kunit *test, struct file *file, unsigned long offset); void kunit_cleanup(struct kunit *test); +void kunit_free_boot_suites(void); void __printf(2, 3) kunit_log_append(struct string_stream *log, const char *fmt, ...); diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 0061d4c7e351..9abaed827584 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -15,6 +15,16 @@ extern struct kunit_suite * const __kunit_suites_end[]; extern struct kunit_suite * const __kunit_init_suites_start[]; extern struct kunit_suite * const __kunit_init_suites_end[]; +static struct kunit_suite_set kunit_boot_suites; + +void kunit_free_boot_suites(void) +{ + if (kunit_boot_suites.start) { + kunit_free_suite_set(kunit_boot_suites); + kunit_boot_suites = (struct kunit_suite_set){ NULL, NULL }; + } +} + static char *action_param; module_param_named(action, action_param, charp, 0400); @@ -409,9 +419,12 @@ int kunit_run_all_tests(void) pr_err("kunit executor: unknown action '%s'\n", action_param); free_out: - if (filter_glob_param || filter_param) - kunit_free_suite_set(suite_set); - else if (init_num_suites > 0) + if (filter_glob_param || filter_param) { + if (err) + kunit_free_suite_set(suite_set); + else + kunit_boot_suites = suite_set; + } else if (init_num_suites > 0) /* Don't use kunit_free_suite_set because suites aren't individually allocated */ kfree(suite_set.start); diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 62eb529824c6..f0e1e02a98d8 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -1056,6 +1056,7 @@ static void __exit kunit_exit(void) kunit_bus_shutdown(); kunit_debugfs_cleanup(); + kunit_free_boot_suites(); } module_exit(kunit_exit); From 18abd88d19ea195e2e1547fca0970c2f91d77a42 Mon Sep 17 00:00:00 2001 From: Dhabaleshwar Das Date: Thu, 21 May 2026 00:00:00 +0530 Subject: [PATCH 393/697] accel/rocket: fix UAF via dangling GEM handle in create_bo [ Upstream commit f706e6a4ce75585af979aec3dcbdce68bc76306b ] rocket_ioctl_create_bo() inserts a GEM handle into the file's IDR via drm_gem_handle_create() early on, then performs several operations that can fail (sgt allocation, drm_mm insert, iommu_map). If any fail after the handle is live, the error path calls drm_gem_shmem_object_free() which kfree's the object without removing the handle from the IDR. This leaves a dangling handle pointing to freed slab memory. Any subsequent ioctl using that handle (PREP_BO, FINI_BO, SUBMIT) calls drm_gem_object_lookup() and dereferences freed memory (UAF). Fix by moving drm_gem_handle_create() to after all fallible operations succeed, matching the pattern used by panfrost, lima, and etnaviv. Also fix drm_mm_insert_node_generic() whose return value was silently overwritten by iommu_map_sgtable() on the next line. Add the missing error check. [tomeu: Move handle creation to the very end] Fixes: 658ebeac3351 ("accel/rocket: Add IOCTL for BO creation") Reported-by: Dhabaleshwar Das Signed-off-by: Dhabaleshwar Das Reviewed-by: Tomeu Vizoso Link: https://patch.msgid.link/20260521165720.2113571-1-tomeu@tomeuvizoso.net Signed-off-by: Tomeu Vizoso Signed-off-by: Sasha Levin --- drivers/accel/rocket/rocket_gem.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c index c3c86e1abd25..b1b24d60973e 100644 --- a/drivers/accel/rocket/rocket_gem.c +++ b/drivers/accel/rocket/rocket_gem.c @@ -78,11 +78,6 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size = args->size; rkt_obj->offset = 0; - ret = drm_gem_handle_create(file, gem_obj, &args->handle); - drm_gem_object_put(gem_obj); - if (ret) - goto err; - sgt = drm_gem_shmem_get_pages_sgt(shmem_obj); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); @@ -94,6 +89,8 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * rkt_obj->size, PAGE_SIZE, 0, 0); mutex_unlock(&rocket_priv->mm_lock); + if (ret) + goto err; ret = iommu_map_sgtable(rocket_priv->domain->domain, rkt_obj->mm.start, @@ -111,8 +108,18 @@ int rocket_ioctl_create_bo(struct drm_device *dev, void *data, struct drm_file * args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); args->dma_address = rkt_obj->mm.start; + ret = drm_gem_handle_create(file, gem_obj, &args->handle); + if (ret) + goto err_unmap; + + drm_gem_object_put(gem_obj); + return 0; +err_unmap: + iommu_unmap(rocket_priv->domain->domain, + rkt_obj->mm.start, rkt_obj->size); + err_remove_node: mutex_lock(&rocket_priv->mm_lock); drm_mm_remove_node(&rkt_obj->mm); From f0fea2b6d5453a11ad11713bbf37561b9b3a7edf Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Tue, 19 May 2026 12:36:14 -0700 Subject: [PATCH 394/697] netfilter: synproxy: refresh tcphdr after skb_ensure_writable [ Upstream commit 92170e6afe927ab2792a3f71902845789c8e31b1 ] synproxy_tstamp_adjust() rewrites the TCP timestamp option in place and then patches the TCP checksum via inet_proto_csum_replace4() on the caller-supplied tcphdr pointer. Both ipv4_synproxy_hook() and ipv6_synproxy_hook() obtain that pointer with skb_header_pointer() before calling in, so it may either alias skb->head directly or point at the caller's on-stack _tcph buffer. Between obtaining the pointer and using it, the function calls skb_ensure_writable(skb, optend), which on a cloned or non-linear skb invokes pskb_expand_head() and frees the old skb->head. After that point the cached th is stale: caller (ipv[46]_synproxy_hook) th = skb_header_pointer(skb, ..., &_tcph) synproxy_tstamp_adjust(skb, protoff, th, ...) skb_ensure_writable(skb, optend) pskb_expand_head() /* kfree(old skb->head) */ ... inet_proto_csum_replace4(&th->check, ...) /* writes into freed head, or into the caller's stack copy leaving the on-wire checksum stale */ The option bytes are written through skb->data and are fine; only the checksum update goes through th and so lands in the wrong place. The result is either a write into freed slab memory or a packet leaving with a checksum that does not match its payload. Fix by re-deriving th from skb->data + protoff immediately after skb_ensure_writable() succeeds, so the subsequent checksum update targets the linear, writable header. Fixes: 48b1de4c110a ("netfilter: add SYNPROXY core/target") Assisted-by: kres (claude-opus-4-7) Signed-off-by: Chris Mason Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/nf_synproxy_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 3fa3f5dfb264..6a851ac4dd04 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -199,6 +199,8 @@ synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, if (skb_ensure_writable(skb, optend)) return 0; + th = (struct tcphdr *)(skb->data + protoff); + while (optoff < optend) { unsigned char *op = skb->data + optoff; From 052468b1c93b2642c851b8a292c1f9c173daa604 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 19 May 2026 20:10:08 +0200 Subject: [PATCH 395/697] netfilter: xt_cpu: prefer raw_smp_processor_id [ Upstream commit c376f07e16c02239ed44cabb97145d03f65b4d15 ] With PREEMPT_RCU we get splat: BUG: using smp_processor_id() in preemptible [..] caller is cpu_mt+0x53/0xd0 net/netfilter/xt_cpu.c:37 CPU: 1 .. Comm: syz.3.1377 #0 PREEMPT(full) Call Trace: dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 check_preemption_disabled+0xd3/0xe0 lib/smp_processor_id.c:47 cpu_mt+0x53/0xd0 net/netfilter/xt_cpu.c:37 [..] Just use raw version instead. This is similar to 14d14a5d2957 ("netfilter: nft_meta: use raw_smp_processor_id()"). Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables") Reported-by: syzbot+690d3e3ffa7335ac10eb@syzkaller.appspotmail.com Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/netfilter/xt_cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_cpu.c b/net/netfilter/xt_cpu.c index 3bdc302a0f91..9cb259902a58 100644 --- a/net/netfilter/xt_cpu.c +++ b/net/netfilter/xt_cpu.c @@ -34,7 +34,7 @@ static bool cpu_mt(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_cpu_info *info = par->matchinfo; - return (info->cpu == smp_processor_id()) ^ info->invert; + return (info->cpu == raw_smp_processor_id()) ^ info->invert; } static struct xt_match cpu_mt_reg __read_mostly = { From bf8e8eac7ede51dc318e06acef5a896dcbba7595 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 19 May 2026 22:52:07 +0200 Subject: [PATCH 396/697] netfilter: ebtables: fix OOB read in compat_mtw_from_user [ Upstream commit f438d1786d657d57790c5d138d6db3fc9fdac392 ] Luxiao Xu says: The function compat_mtw_from_user() converts ebtables extensions from 32-bit user structures to kernel native structures. However, it lacks proper validation of the user-supplied match_size/target_size. When certain extensions are processed, the kernel-side translation logic may perform memory accesses based on the extension's expected size. If the user provides a size smaller than what the extension requires, it results in an out-of-bounds read as reported by KASAN. This fix introduces a check to ensure match_size is at least as large as the extension's required compatsize. This covers matches, watchers, and targets, while maintaining compatibility with standard targets. AFAIU this is relevant for matches that need to go though match->compat_from_user() call. Those that use plain memcpy with the user-provided size are ok because the caller checks that size vs the start of the next rule entry offset (which itself is checked vs. total size copied from userspace). The ->compat_from_user() callbacks assume they can read compatsize bytes, so they need this extra check. Based on an earlier patch from Luxiao Xu. Fixes: 81e675c227ec ("netfilter: ebtables: add CONFIG_COMPAT support") Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Reviewed-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- net/bridge/netfilter/ebtables.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 77df9e856c2e..ecd7baa25d72 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1956,6 +1956,25 @@ enum compat_mwt { EBT_COMPAT_TARGET, }; +static bool match_size_ok(const struct xt_match *match, unsigned int match_size) +{ + u16 csize; + + if (match->matchsize == -1) /* cannot validate ebt_among */ + return true; + + csize = match->compatsize ? : match->matchsize; + + return match_size >= csize; +} + +static bool tgt_size_ok(const struct xt_target *tgt, unsigned int tgt_size) +{ + u16 csize = tgt->compatsize ? : tgt->targetsize; + + return tgt_size >= csize; +} + static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt, enum compat_mwt compat_mwt, struct ebt_entries_buf_state *state, @@ -1981,6 +2000,11 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt, if (IS_ERR(match)) return PTR_ERR(match); + if (!match_size_ok(match, match_size)) { + module_put(match->me); + return -EINVAL; + } + off = ebt_compat_match_offset(match, match_size); if (dst) { if (match->compat_from_user) @@ -2000,6 +2024,12 @@ static int compat_mtw_from_user(const struct compat_ebt_entry_mwt *mwt, mwt->u.revision); if (IS_ERR(wt)) return PTR_ERR(wt); + + if (!tgt_size_ok(wt, match_size)) { + module_put(wt->me); + return -EINVAL; + } + off = xt_compat_target_offset(wt); if (dst) { From 96bea2a7baac4a1137c188dc7610184487ab30a7 Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Mon, 11 May 2026 16:37:56 +0200 Subject: [PATCH 397/697] netfilter: nf_tables: fix dst corruption in same register operation [ Upstream commit 18014147d3ee7831dce53fe65d7fc8d428b02552 ] For lshift and rshift, the shift operations are performed in a loop over 32-bit words. The loop calculates the shifted value and write it to dst, and then immediately reads from src to calculate the carry for the next iteration. Because src and dst could point to the same memory location, the carry is incorrectly calculated using the newly modified dst value instead of the original src value. Adding a temporary local variable to cache the original value before writing to dst and using it for the carry calculation solves the problem. In addition, partial overlap is rejected from control plane for all kind of operations including byteorder. This was tested with the following bytecode: table test_table ip flags 0 use 1 handle 1 ip test_table test_chain use 3 type filter hook input prio 0 policy accept packets 0 bytes 0 flags 1 ip test_table test_chain 2 [ immediate reg 1 0x44332211 0x88776655 ] [ bitwise reg 1 = ( reg 1 << 0x08000000 ) ] [ cmp eq reg 1 0x66443322 0x00887766 ] [ counter pkts 0 bytes 0 ] ip test_table test_chain 4 3 [ immediate reg 1 0x44332211 0x88776655 ] [ bitwise reg 1 = ( reg 1 << 0x08000000 ) ] [ cmp eq reg 1 0x55443322 0x00887766 ] [ counter pkts 21794 bytes 1917798 ] Fixes: 567d746b55bc ("netfilter: bitwise: add support for shifts.") Acked-by: Jeremy Sowden Signed-off-by: Fernando Fernandez Mancera Signed-off-by: Florian Westphal Signed-off-by: Sasha Levin --- include/net/netfilter/nf_tables.h | 7 +++++++ net/netfilter/nft_bitwise.c | 18 ++++++++++++++---- net/netfilter/nft_byteorder.c | 13 ++++++++++--- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 4dc080f7f27c..b35e8c02fadc 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -188,6 +188,13 @@ static inline u64 nft_reg_load64(const u32 *sreg) return get_unaligned((u64 *)sreg); } +static inline bool nft_reg_overlap(u8 src, u8 dst, u32 len) +{ + unsigned int n = DIV_ROUND_UP(len, sizeof(u32)); + + return src != dst && src < dst + n && dst < src + n; +} + static inline void nft_data_copy(u32 *dst, const struct nft_data *src, unsigned int len) { diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index af990c600745..1afb36fb5994 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -43,8 +43,10 @@ static void nft_bitwise_eval_lshift(u32 *dst, const u32 *src, u32 carry = 0; for (i = DIV_ROUND_UP(priv->len, sizeof(u32)); i > 0; i--) { - dst[i - 1] = (src[i - 1] << shift) | carry; - carry = src[i - 1] >> (BITS_PER_TYPE(u32) - shift); + u32 tmp_src = src[i - 1]; + + dst[i - 1] = (tmp_src << shift) | carry; + carry = tmp_src >> (BITS_PER_TYPE(u32) - shift); } } @@ -56,8 +58,10 @@ static void nft_bitwise_eval_rshift(u32 *dst, const u32 *src, u32 carry = 0; for (i = 0; i < DIV_ROUND_UP(priv->len, sizeof(u32)); i++) { - dst[i] = carry | (src[i] >> shift); - carry = src[i] << (BITS_PER_TYPE(u32) - shift); + u32 tmp_src = src[i]; + + dst[i] = carry | (tmp_src >> shift); + carry = tmp_src << (BITS_PER_TYPE(u32) - shift); } } @@ -235,6 +239,9 @@ static int nft_bitwise_init_bool(const struct nft_ctx *ctx, &priv->sreg2, priv->len); if (err < 0) return err; + + if (nft_reg_overlap(priv->sreg2, priv->dreg, priv->len)) + return -EINVAL; } return 0; @@ -265,6 +272,9 @@ static int nft_bitwise_init(const struct nft_ctx *ctx, if (err < 0) return err; + if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len)) + return -EINVAL; + if (tb[NFTA_BITWISE_OP]) { priv->op = ntohl(nla_get_be32(tb[NFTA_BITWISE_OP])); switch (priv->op) { diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index af9206a3afd1..5e7a7841b789 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -144,9 +144,16 @@ static int nft_byteorder_init(const struct nft_ctx *ctx, if (err < 0) return err; - return nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG], - &priv->dreg, NULL, NFT_DATA_VALUE, - priv->len); + err = nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG], + &priv->dreg, NULL, NFT_DATA_VALUE, + priv->len); + if (err < 0) + return err; + + if (nft_reg_overlap(priv->sreg, priv->dreg, priv->len)) + return -EINVAL; + + return 0; } static int nft_byteorder_dump(struct sk_buff *skb, From 37a1c268c2c8090bf4dc552d732bd23ba36f8eb0 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 20 May 2026 09:00:21 -0700 Subject: [PATCH 398/697] tun: free page on short-frame rejection in tun_xdp_one() [ Upstream commit f4feb1e20058e407cb00f45aff47f5b7e19a6bbf ] tun_xdp_one() returns -EINVAL on a frame shorter than ETH_HLEN without freeing the page that vhost_net_build_xdp() allocated for it. tun_sendmsg() discards that -EINVAL and still returns total_len, so vhost_tx_batch() takes the success path and never frees the page; each short frame in a batch leaks one page-frag chunk. A local process that can open /dev/net/tun and /dev/vhost-net can hit this path: it attaches a tun/tap device as the vhost-net backend and feeds TX descriptors whose length minus the virtio-net header is below ETH_HLEN. Each kick leaks the page-frag chunks for that batch, and a tight submission loop exhausts host memory and triggers an OOM panic. Free the page before returning -EINVAL, matching the XDP-program error path in the same function. Fixes: 049584807f1d ("tun: add missing verification for short frame") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Dongli Zhang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260520160020.375349-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/tun.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8192740357a0..afba37965ce3 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2392,8 +2392,10 @@ static int tun_xdp_one(struct tun_struct *tun, bool skb_xdp = false; struct page *page; - if (unlikely(datasize < ETH_HLEN)) + if (unlikely(datasize < ETH_HLEN)) { + put_page(virt_to_head_page(xdp->data)); return -EINVAL; + } xdp_prog = rcu_dereference(tun->xdp_prog); if (xdp_prog) { From aa308e9dbb9acb17cacdbbce9e4504f69bac8385 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 21 May 2026 09:33:13 -0700 Subject: [PATCH 399/697] tun: free page on build_skb failure in tun_xdp_one() [ Upstream commit aa8963fdce667a42fb7f0bdd2909fadcab02f9a8 ] When build_skb() fails in tun_xdp_one(), the function sets ret to -ENOMEM and jumps to the out label, which returns without freeing the page that vhost_net_build_xdp() allocated for the frame. As with the short-frame rejection path, tun_sendmsg() discards the per-buffer error and still returns total_len, so vhost_tx_batch() takes the success path and never frees the page. Each build_skb() failure in a batch leaks one page-frag chunk. Free the page before taking the error path, matching the put_page() the other error exits of tun_xdp_one() already perform. Fixes: 043d222f93ab ("tuntap: accept an array of XDP buffs through sendmsg()") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Dongli Zhang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260521163312.1479805-2-bestswngs@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/tun.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index afba37965ce3..9a767da38c71 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2437,6 +2437,7 @@ static int tun_xdp_one(struct tun_struct *tun, build: skb = build_skb(xdp->data_hard_start, buflen); if (!skb) { + put_page(virt_to_head_page(xdp->data)); ret = -ENOMEM; goto out; } From ef3b3ea864d0aecfc8bcec471d567edfe7b6572e Mon Sep 17 00:00:00 2001 From: Ziyu Zhang Date: Wed, 20 May 2026 00:56:36 +0800 Subject: [PATCH 400/697] vsock: keep poll shutdown state consistent [ Upstream commit aae9d8a5528b8ee9ff8dc5d3558b8a9f852a724a ] vsock_poll() reads vsk->peer_shutdown before taking the socket lock to set EPOLLHUP and EPOLLRDHUP, then reads it again after taking the lock to report EOF readability. A shutdown packet can update peer_shutdown while poll is waiting for the lock, so one poll invocation can report EOF readability without the corresponding HUP/RDHUP bits. For connectible sockets, take one peer_shutdown snapshot after lock_sock() and use it for all peer-shutdown-derived poll bits. For datagram sockets, which do not take lock_sock() in poll(), take one lockless READ_ONCE() snapshot and pair it with WRITE_ONCE() on the writer side. This keeps the peer-shutdown-derived bits internally consistent for each poll pass. Fixes: d021c344051a ("VSOCK: Introduce VM Sockets") Signed-off-by: Ziyu Zhang Link: https://patch.msgid.link/20260519165636.62542-1-ziyuzhang201@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/vmw_vsock/af_vsock.c | 49 ++++++++++++++++--------- net/vmw_vsock/hyperv_transport.c | 9 +++-- net/vmw_vsock/virtio_transport_common.c | 14 ++++--- net/vmw_vsock/vmci_transport.c | 8 ++-- 4 files changed, 52 insertions(+), 28 deletions(-) diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 9d0e1915abbe..2db48b53e47c 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -523,7 +523,7 @@ int vsock_assign_transport(struct vsock_sock *vsk, struct vsock_sock *psk) */ sock_reset_flag(sk, SOCK_DONE); sk->sk_state = TCP_CLOSE; - vsk->peer_shutdown = 0; + WRITE_ONCE(vsk->peer_shutdown, 0); } if (sk->sk_type == SOCK_SEQPACKET) { @@ -814,7 +814,7 @@ static struct sock *__vsock_create(struct net *net, vsk->rejected = false; vsk->sent_request = false; vsk->ignore_connecting_rst = false; - vsk->peer_shutdown = 0; + WRITE_ONCE(vsk->peer_shutdown, 0); INIT_DELAYED_WORK(&vsk->connect_work, vsock_connect_timeout); INIT_DELAYED_WORK(&vsk->pending_work, vsock_pending_work); @@ -1122,6 +1122,25 @@ static int vsock_shutdown(struct socket *sock, int mode) return err; } +static __poll_t vsock_poll_shutdown(struct sock *sk, u32 peer_shutdown) +{ + __poll_t mask = 0; + + /* INET sockets treat local write shutdown and peer write shutdown as a + * case of EPOLLHUP set. + */ + if (sk->sk_shutdown == SHUTDOWN_MASK || + ((sk->sk_shutdown & SEND_SHUTDOWN) && + (peer_shutdown & SEND_SHUTDOWN))) + mask |= EPOLLHUP; + + if (sk->sk_shutdown & RCV_SHUTDOWN || + peer_shutdown & SEND_SHUTDOWN) + mask |= EPOLLRDHUP; + + return mask; +} + static __poll_t vsock_poll(struct file *file, struct socket *sock, poll_table *wait) { @@ -1139,24 +1158,17 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, /* Signify that there has been an error on this socket. */ mask |= EPOLLERR; - /* INET sockets treat local write shutdown and peer write shutdown as a - * case of EPOLLHUP set. - */ - if ((sk->sk_shutdown == SHUTDOWN_MASK) || - ((sk->sk_shutdown & SEND_SHUTDOWN) && - (vsk->peer_shutdown & SEND_SHUTDOWN))) { - mask |= EPOLLHUP; - } - - if (sk->sk_shutdown & RCV_SHUTDOWN || - vsk->peer_shutdown & SEND_SHUTDOWN) { - mask |= EPOLLRDHUP; - } - if (sk_is_readable(sk)) mask |= EPOLLIN | EPOLLRDNORM; if (sock->type == SOCK_DGRAM) { + u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown); + + /* DGRAM sockets do not take lock_sock() in poll(), so use one + * lockless snapshot for all shutdown-derived mask bits. + */ + mask |= vsock_poll_shutdown(sk, peer_shutdown); + /* For datagram sockets we can read if there is something in * the queue and write as long as the socket isn't shutdown for * sending. @@ -1171,6 +1183,7 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, } else if (sock_type_connectible(sk->sk_type)) { const struct vsock_transport *transport; + u32 peer_shutdown; lock_sock(sk); @@ -1203,8 +1216,10 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, * terminated should also be considered read, and we check the * shutdown flag for that. */ + peer_shutdown = READ_ONCE(vsk->peer_shutdown); + mask |= vsock_poll_shutdown(sk, peer_shutdown); if (sk->sk_shutdown & RCV_SHUTDOWN || - vsk->peer_shutdown & SEND_SHUTDOWN) { + peer_shutdown & SEND_SHUTDOWN) { mask |= EPOLLIN | EPOLLRDNORM; } diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index f9dc9b4d3023..4da752b47b11 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -264,7 +264,7 @@ static void hvs_do_close_lock_held(struct vsock_sock *vsk, struct sock *sk = sk_vsock(vsk); sock_set_flag(sk, SOCK_DONE); - vsk->peer_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK); if (vsock_stream_has_data(vsk) <= 0) sk->sk_state = TCP_CLOSING; sk->sk_state_change(sk); @@ -593,7 +593,9 @@ static int hvs_update_recv_data(struct hvsock *hvs) return -EIO; if (payload_len == 0) - hvs->vsk->peer_shutdown |= SEND_SHUTDOWN; + WRITE_ONCE(hvs->vsk->peer_shutdown, + READ_ONCE(hvs->vsk->peer_shutdown) | + SEND_SHUTDOWN); hvs->recv_data_len = payload_len; hvs->recv_data_off = 0; @@ -736,7 +738,8 @@ static s64 hvs_stream_has_data(struct vsock_sock *vsk) return ret; return hvs->recv_data_len; case 0: - vsk->peer_shutdown |= SEND_SHUTDOWN; + WRITE_ONCE(vsk->peer_shutdown, + READ_ONCE(vsk->peer_shutdown) | SEND_SHUTDOWN); ret = 0; break; default: /* -1 */ diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index ed42e08798a9..1e07d3b1a0e8 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -1206,7 +1206,7 @@ static void virtio_transport_do_close(struct vsock_sock *vsk, struct sock *sk = sk_vsock(vsk); sock_set_flag(sk, SOCK_DONE); - vsk->peer_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK); if (vsock_stream_has_data(vsk) <= 0) sk->sk_state = TCP_CLOSING; sk->sk_state_change(sk); @@ -1409,12 +1409,15 @@ virtio_transport_recv_connected(struct sock *sk, case VIRTIO_VSOCK_OP_CREDIT_UPDATE: sk->sk_write_space(sk); break; - case VIRTIO_VSOCK_OP_SHUTDOWN: + case VIRTIO_VSOCK_OP_SHUTDOWN: { + u32 peer_shutdown = READ_ONCE(vsk->peer_shutdown); + if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_RCV) - vsk->peer_shutdown |= RCV_SHUTDOWN; + peer_shutdown |= RCV_SHUTDOWN; if (le32_to_cpu(hdr->flags) & VIRTIO_VSOCK_SHUTDOWN_SEND) - vsk->peer_shutdown |= SEND_SHUTDOWN; - if (vsk->peer_shutdown == SHUTDOWN_MASK) { + peer_shutdown |= SEND_SHUTDOWN; + WRITE_ONCE(vsk->peer_shutdown, peer_shutdown); + if (peer_shutdown == SHUTDOWN_MASK) { if (vsock_stream_has_data(vsk) <= 0 && !sock_flag(sk, SOCK_DONE)) { (void)virtio_transport_reset(vsk, NULL); virtio_transport_do_close(vsk, true); @@ -1429,6 +1432,7 @@ virtio_transport_recv_connected(struct sock *sk, if (le32_to_cpu(virtio_vsock_hdr(skb)->flags)) sk->sk_state_change(sk); break; + } case VIRTIO_VSOCK_OP_RST: virtio_transport_do_close(vsk, true); break; diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 4cd11f355e9d..443125e48f24 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -811,7 +811,7 @@ static void vmci_transport_handle_detach(struct sock *sk) /* On a detach the peer will not be sending or receiving * anymore. */ - vsk->peer_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK); /* We should not be sending anymore since the peer won't be * there to receive, but we can still receive if there is data @@ -1534,7 +1534,9 @@ static int vmci_transport_recv_connected(struct sock *sk, if (pkt->u.mode) { vsk = vsock_sk(sk); - vsk->peer_shutdown |= pkt->u.mode; + WRITE_ONCE(vsk->peer_shutdown, + READ_ONCE(vsk->peer_shutdown) | + pkt->u.mode); sk->sk_state_change(sk); } break; @@ -1551,7 +1553,7 @@ static int vmci_transport_recv_connected(struct sock *sk, * a clean shutdown. */ sock_set_flag(sk, SOCK_DONE); - vsk->peer_shutdown = SHUTDOWN_MASK; + WRITE_ONCE(vsk->peer_shutdown, SHUTDOWN_MASK); if (vsock_stream_has_data(vsk) <= 0) sk->sk_state = TCP_CLOSING; From ca5e366296417f399b73fa5ce39f9f37770d1e1c Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Wed, 20 May 2026 19:22:35 +0200 Subject: [PATCH 401/697] net: netlink: fix sending unassigned nsid after assigned one [ Upstream commit 70f8592ee90585272018a725054b6eb2ab7e99ca ] If the current skb is not shared, it is re-used directly for all the sockets subscribed to the notification. If we have remote all-nsid socket receiving a message first, then the 'nsid_is_set' will be set to 'true'. If the nsid is NOT_ASSIGNED for the next socket in the list, the 'nsid_is_set' will remain 'true' and the negative value is be delivered to the user space. All subsequent nsid values will be delivered as well, since there is no code path that sets the flag back to 'false'. Fix that by always dropping the flag to 'false' first. Fixes: 7212462fa6fd ("netlink: don't send unknown nsid") Signed-off-by: Ilya Maximets Acked-by: Nicolas Dichtel Link: https://patch.msgid.link/20260520172317.175168-2-i.maximets@ovn.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/netlink/af_netlink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2b46c0cd752a..0e6dfd01d9b4 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1484,6 +1484,7 @@ static void do_one_broadcast(struct sock *sk, p->skb2 = NULL; goto out; } + NETLINK_CB(p->skb2).nsid_is_set = false; NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net); if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED) NETLINK_CB(p->skb2).nsid_is_set = true; From bcd0d19db3e66d457c18f8e54302115cbd175f31 Mon Sep 17 00:00:00 2001 From: Ilya Maximets Date: Wed, 20 May 2026 19:22:36 +0200 Subject: [PATCH 402/697] net: netlink: don't set nsid on local notifications [ Upstream commit 88b126b39f9757e9debc322d4679239e9af089c7 ] In most cases, notifications on sockets with NETLINK_LISTEN_ALL_NSID do not contain NSID in their ancillary data in case the event is local to the listener. However, when a self-referential NSID is allocated for a namespace, every local notification starts sending this ID to the user space. This is problematic, because the listener cannot tell if those notifications are local or not anymore without making extra requests to figure out if the provided NSID is local or not. The listener can also not figure out the local NSID beforehand as it can be allocated at any point in time by other processes, changing the structure of the future notifications for everyone. The value is practically not useful, since it's the namespace's own ID that the application has to obtain from other sources in order to figure out if it's the same or not. So, for the application it's just an extra busy work with no benefits. Moreover, applications that do not know about this quirk may be mishandling notifications with NSID set as notifications from remote namespaces. This is the case for ovs-vswitchd and the iproute2's 'ip monitor' that stops printing 'current' and starts printing the nsid number mid-session. Lack of clear documentation for this behavior is also not helping. A search though open-source projects doesn't reveal any projects that use NETNSA_NSID_NOT_ASSIGNED and rely on metadata to contain self-referential NSIDs (expected, since the value is not useful). Quite the opposite, as already mentioned, there are few applications that rely on NSID to not be present in local events. Since the value is not useful and actively harmful in some cases, let's not report it for local events, making the notifications more consistent. Also adding some blank lines for readability. Fixes: 59324cf35aba ("netlink: allow to listen "all" netns") Reported-by: Matteo Perin Signed-off-by: Ilya Maximets Acked-by: Nicolas Dichtel Link: https://patch.msgid.link/20260520172317.175168-3-i.maximets@ovn.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/netlink/af_netlink.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 0e6dfd01d9b4..1d1b3bd54a91 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1484,10 +1484,14 @@ static void do_one_broadcast(struct sock *sk, p->skb2 = NULL; goto out; } + NETLINK_CB(p->skb2).nsid_is_set = false; - NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net); - if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED) - NETLINK_CB(p->skb2).nsid_is_set = true; + if (!net_eq(sock_net(sk), p->net)) { + NETLINK_CB(p->skb2).nsid = peernet2id(sock_net(sk), p->net); + if (NETLINK_CB(p->skb2).nsid != NETNSA_NSID_NOT_ASSIGNED) + NETLINK_CB(p->skb2).nsid_is_set = true; + } + val = netlink_broadcast_deliver(sk, p->skb2); if (val < 0) { netlink_overrun(sk); From 55cba6b883b41e5922c00ba9d4e3262131f46f1b Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Thu, 21 May 2026 16:56:39 +0200 Subject: [PATCH 403/697] net/smc: Do not re-initialize smc hashtables [ Upstream commit 9e4389b0038781f19f97895186ed941ff8ac1678 ] INIT_HLIST_HEAD(&smc_v*_hashinfo.ht) are called after smc_nl_init(), proto_register() and sock_register(). This can lead to smc_v*_hashinfo.ht being reset even though hash entries already exist and are being used, possibly resulting in a corrupted list. Remove unnecessary and dangerous re-initialisation of smc_v*_hashinfo.ht in smc_init(); it is implicitly initialised to zero anyhow. Add HLIST_HEAD_INIT to the definitions for clarity. Fixes: f16a7dd5cf27 ("smc: netlink interface for SMC sockets") Suggested-by: Halil Pasic Signed-off-by: Alexandra Winter Acked-by: Halil Pasic Reviewed-by: Mahanta Jambigi Link: https://patch.msgid.link/20260521145639.10317-1-wintera@linux.ibm.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/smc/af_smc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 5915fcdef743..21d0c62bcf46 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -188,10 +188,12 @@ static bool smc_hs_congested(const struct sock *sk) struct smc_hashinfo smc_v4_hashinfo = { .lock = __RW_LOCK_UNLOCKED(smc_v4_hashinfo.lock), + .ht = HLIST_HEAD_INIT, }; struct smc_hashinfo smc_v6_hashinfo = { .lock = __RW_LOCK_UNLOCKED(smc_v6_hashinfo.lock), + .ht = HLIST_HEAD_INIT, }; int smc_hash_sk(struct sock *sk) @@ -3595,8 +3597,6 @@ static int __init smc_init(void) pr_err("%s: sock_register fails with %d\n", __func__, rc); goto out_proto6; } - INIT_HLIST_HEAD(&smc_v4_hashinfo.ht); - INIT_HLIST_HEAD(&smc_v6_hashinfo.ht); rc = smc_ib_register_client(); if (rc) { From 9817369243380e287ebe5525411557eaa3aa2a79 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 21 May 2026 07:11:45 -0700 Subject: [PATCH 404/697] net/iucv: fix locking in .getsockopt [ Upstream commit 3589d20a666caf30ad100c960a2de7de390fce88 ] Mirror iucv_sock_setsockopt() and wrap the whole switch in lock_sock()/release_sock(). The pre-existing SO_MSGLIMIT-only lock becomes redundant and is removed. Any AF_IUCV HIPER user can potentially crash the kernel by racing recvmsg() with getsockopt(SO_MSGSIZE): the SO_MSGSIZE arm dereferences iucv->hs_dev->mtu after iucv_sock_close() (called from the racing recvmsg()) has set hs_dev to NULL, producing a NULL pointer dereference oops. Suggested-by: Stanislav Fomichev Fixes: 51363b8751a6 ("af_iucv: allow retrieval of maximum message size") Signed-off-by: Breno Leitao Reviewed-by: Alexandra Winter Tested-by: Alexandra Winter Link: https://patch.msgid.link/20260521-af_iucv_fix2-v1-1-f16b1c510aa9@debian.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/iucv/af_iucv.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index 6c717a7ef292..c66b90c912e7 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -1538,7 +1538,7 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct iucv_sock *iucv = iucv_sk(sk); unsigned int val; - int len; + int len, rc; if (level != SOL_IUCV) return -ENOPROTOOPT; @@ -1551,26 +1551,34 @@ static int iucv_sock_getsockopt(struct socket *sock, int level, int optname, len = min_t(unsigned int, len, sizeof(int)); + rc = 0; + + lock_sock(sk); switch (optname) { case SO_IPRMDATA_MSG: val = (iucv->flags & IUCV_IPRMDATA) ? 1 : 0; break; case SO_MSGLIMIT: - lock_sock(sk); val = (iucv->path != NULL) ? iucv->path->msglim /* connected */ : iucv->msglimit; /* default */ - release_sock(sk); break; case SO_MSGSIZE: - if (sk->sk_state == IUCV_OPEN) - return -EBADFD; + if (sk->sk_state == IUCV_OPEN) { + rc = -EBADFD; + break; + } val = (iucv->hs_dev) ? iucv->hs_dev->mtu - sizeof(struct af_iucv_trans_hdr) - ETH_HLEN : 0x7fffffff; break; default: - return -ENOPROTOOPT; + rc = -ENOPROTOOPT; + break; } + release_sock(sk); + + if (rc) + return rc; if (put_user(len, optlen)) return -EFAULT; From 475f2b37a78f4c698967a7f14f325f04e24c9175 Mon Sep 17 00:00:00 2001 From: David Jeffery Date: Fri, 15 May 2026 14:09:41 -0400 Subject: [PATCH 405/697] scsi: core: Run queues for all non-SDEV_DEL devices from scsi_run_host_queues [ Upstream commit 7205b58702273baf21d6ba7992e6ba15852325f7 ] While a SCSI host is in a recovery state, scsi_mq_requeue_cmd() will not set the requeue list for a requeued command to be kicked in the future. The expectation is a call to scsi_run_host_queues() will kick all SCSI devices once the recovery state is cleared. However, scsi_run_host_queues() uses shost_for_each_device() which uses scsi_device_get() and so will ignore devices in a partially removed state like SDEV_CANCEL. But these devices may also have requeued requests, leaving their requests stuck from not being kicked and causing the removal process of the device to hang. scsi_run_host_queues() needs to run against more devices than the macro shost_for_each_device() allows. Instead of using the too limiting scsi_device_get() state checks, only ignore devices in SDEV_DEL state or when unable to acquire a reference. Attempt to run the queues for all other devices when scsi_run_host_queues() is called. Fixes: 8b566edbdbfb ("scsi: core: Only kick the requeue list if necessary") Signed-off-by: David Jeffery Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260515180941.9698-1-djeffery@redhat.com Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/scsi/scsi_lib.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7ddb73cd6d9f..3f7ba6d3987f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -572,10 +572,33 @@ void scsi_requeue_run_queue(struct work_struct *work) void scsi_run_host_queues(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev, *prev = NULL; + unsigned long flags; - shost_for_each_device(sdev, shost) + spin_lock_irqsave(shost->host_lock, flags); + __shost_for_each_device(sdev, shost) { + /* + * Only skip devices so deep into removal they will never need + * another kick to their queues. Thus scsi_device_get() cannot + * be used as it would skip devices in SDEV_CANCEL state which + * may need a queue kick. + */ + if (sdev->sdev_state == SDEV_DEL || + !get_device(&sdev->sdev_gendev)) + continue; + spin_unlock_irqrestore(shost->host_lock, flags); + + if (prev) + put_device(&prev->sdev_gendev); scsi_run_queue(sdev->request_queue); + + prev = sdev; + + spin_lock_irqsave(shost->host_lock, flags); + } + spin_unlock_irqrestore(shost->host_lock, flags); + if (prev) + put_device(&prev->sdev_gendev); } static void scsi_uninit_cmd(struct scsi_cmnd *cmd) From a7f4eefb6e1458431eef9fa20fb363320d185f76 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 21 May 2026 12:21:47 +0000 Subject: [PATCH 406/697] ipv4: free net->ipv4.sysctl_local_reserved_ports after unregister_net_sysctl_table() [ Upstream commit 87a1e0fe7776da7ab411be332b4be58ac8840d10 ] ipv4_sysctl_exit_net() is currently freeing net->ipv4.sysctl_local_reserved_ports too soon. Only after unregister_net_sysctl_table() we can be sure no threads can possibly use the sysctls, including /proc/sys/net/ipv4/ip_local_reserved_ports. Fixes: 122ff243f5f1 ("ipv4: make ip_local_reserved_ports per netns") Reported-by: Ji'an Zhou Signed-off-by: Eric Dumazet Cc: Cong Wang Reviewed-by: Jason Xing Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260521122147.3584624-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/sysctl_net_ipv4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 0f1dd75dbf37..ce9b077343ca 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1669,10 +1669,10 @@ static __net_exit void ipv4_sysctl_exit_net(struct net *net) { const struct ctl_table *table; - kfree(net->ipv4.sysctl_local_reserved_ports); table = net->ipv4.ipv4_hdr->ctl_table_arg; unregister_net_sysctl_table(net->ipv4.ipv4_hdr); kfree(table); + kfree(net->ipv4.sysctl_local_reserved_ports); } static __net_initdata struct pernet_operations ipv4_sysctl_ops = { From e13922bb97b4e6f94f8ac02d034f2d4bd65eeb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Fri, 22 May 2026 22:09:40 -0300 Subject: [PATCH 407/697] ALSA: pcm: oss: Fix setup list UAF on proc write error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 4cc54bdd54b337e77115be5b55577d1c58608eae ] snd_pcm_oss_proc_write() links a newly allocated setup entry into the OSS setup list before duplicating the task name. If the task-name allocation fails, the error path frees the already linked entry and leaves setup_list pointing at freed memory. A later OSS device open can then walk the stale list entry in snd_pcm_oss_look_for_setup() and dereference freed memory. Allocate the task name and initialize the setup entry before publishing the entry on setup_list. Also fetch the initial proc read iterator only after taking setup_mutex, so all setup_list traversal follows the same list lifetime rules. Reported-by: syzbot+8e498074a794999eb41c@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/6a1062b7.170a0220.35b2b7.0003.GAE@google.com Closes: https://syzkaller.appspot.com/bug?extid=8e498074a794999eb41c Fixes: 060d77b9c04a ("[ALSA] Fix / clean up PCM-OSS setup hooks") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260522-alsa-pcm-oss-setup-uaf-v1-1-40bdcc4d17e8@gmail.com Signed-off-by: Takashi Iwai Signed-off-by: Sasha Levin --- sound/core/oss/pcm_oss.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 9b5a3def8d2c..59d5153d1113 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -2965,8 +2965,10 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { struct snd_pcm_str *pstr = entry->private_data; - struct snd_pcm_oss_setup *setup = pstr->oss.setup_list; + struct snd_pcm_oss_setup *setup; + guard(mutex)(&pstr->oss.setup_mutex); + setup = pstr->oss.setup_list; while (setup) { snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n", setup->task_name, @@ -3051,6 +3053,13 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, buffer->error = -ENOMEM; return; } + template.task_name = kstrdup(task_name, GFP_KERNEL); + if (!template.task_name) { + kfree(setup); + buffer->error = -ENOMEM; + return; + } + *setup = template; if (pstr->oss.setup_list == NULL) pstr->oss.setup_list = setup; else { @@ -3058,12 +3067,7 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry, setup1->next; setup1 = setup1->next); setup1->next = setup; } - template.task_name = kstrdup(task_name, GFP_KERNEL); - if (! template.task_name) { - kfree(setup); - buffer->error = -ENOMEM; - return; - } + continue; } *setup = template; } From f0ac76e3d55e9e7f0e6b26610e3de49026a4a196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Tue, 19 May 2026 13:51:47 -0300 Subject: [PATCH 408/697] ASoC: Intel: bytcht_es8316: Fix MCLK leak on init errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit afb2a3a9d8369d18122a0d7cd294eba9a98259c6 ] byt_cht_es8316_init() enables MCLK before configuring the codec sysclk and creating the headset jack. If either of those later steps fails, the function returns without disabling MCLK, leaving the clock enabled after card registration fails. Track whether this driver enabled MCLK and disable it on the init error paths. Add the matching DAI link exit callback so the same clock enable is also balanced when ASoC cleans up a successfully initialized link. Fixes: a03bdaa565cb ("ASoC: Intel: add machine driver for BYT/CHT + ES8316") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260519-asoc-bytcht-es8316-mclk-leak-v1-1-b4a11cdc2afd@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/intel/boards/bytcht_es8316.c | 29 ++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 3b5f63112237..25044c0f4703 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -40,6 +40,7 @@ struct byt_cht_es8316_private { struct gpio_desc *speaker_en_gpio; struct device *codec_dev; bool speaker_en; + bool mclk_enabled; }; enum { @@ -170,6 +171,15 @@ static struct snd_soc_jack_pin byt_cht_es8316_jack_pins[] = { }, }; +static void byt_cht_es8316_disable_mclk(struct byt_cht_es8316_private *priv) +{ + if (!priv->mclk_enabled) + return; + + clk_disable_unprepare(priv->mclk); + priv->mclk_enabled = false; +} + static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) { struct snd_soc_component *codec = snd_soc_rtd_to_codec(runtime, 0)->component; @@ -226,12 +236,14 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) ret = clk_prepare_enable(priv->mclk); if (ret) dev_err(card->dev, "unable to enable MCLK\n"); + else + priv->mclk_enabled = true; ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_codec(runtime, 0), 0, 19200000, SND_SOC_CLOCK_IN); if (ret < 0) { dev_err(card->dev, "can't set codec clock %d\n", ret); - return ret; + goto err_disable_mclk; } ret = snd_soc_card_jack_new_pins(card, "Headset", @@ -240,13 +252,25 @@ static int byt_cht_es8316_init(struct snd_soc_pcm_runtime *runtime) ARRAY_SIZE(byt_cht_es8316_jack_pins)); if (ret) { dev_err(card->dev, "jack creation failed %d\n", ret); - return ret; + goto err_disable_mclk; } snd_jack_set_key(priv->jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); snd_soc_component_set_jack(codec, &priv->jack, NULL); return 0; + +err_disable_mclk: + byt_cht_es8316_disable_mclk(priv); + return ret; +} + +static void byt_cht_es8316_exit(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_soc_card *card = runtime->card; + struct byt_cht_es8316_private *priv = snd_soc_card_get_drvdata(card); + + byt_cht_es8316_disable_mclk(priv); } static int byt_cht_es8316_codec_fixup(struct snd_soc_pcm_runtime *rtd, @@ -352,6 +376,7 @@ static struct snd_soc_dai_link byt_cht_es8316_dais[] = { | SND_SOC_DAIFMT_CBC_CFC, .be_hw_params_fixup = byt_cht_es8316_codec_fixup, .init = byt_cht_es8316_init, + .exit = byt_cht_es8316_exit, SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), }, }; From 2a15a03e58b00484e7f48aabb3a309735ca0a572 Mon Sep 17 00:00:00 2001 From: Prathamesh Deshpande Date: Wed, 6 May 2026 01:00:31 +0100 Subject: [PATCH 409/697] net/mlx5: HWS: Reject unsupported remove-header action [ Upstream commit 86f1d0f063e423a5c1982db1e5e7a8eac511e603 ] mlx5_cmd_hws_packet_reformat_alloc() handles MLX5_REFORMAT_TYPE_REMOVE_HDR by looking up a matching HWS remove-header action. If mlx5_fs_get_action_remove_header_vlan() returns NULL, the code only logs an error and continues. The function then returns success with a NULL HWS action stored in the packet-reformat object. Return an error when no matching remove-header action is available. Fixes: aecd9d1020e3 ("net/mlx5: fs, add HWS packet reformat API function") Signed-off-by: Prathamesh Deshpande Reviewed-by: Simon Horman Reviewed-by: Yevgeny Kliteynik Acked-by: Tariq Toukan Link: https://patch.msgid.link/20260506000054.51797-1-prathameshdeshpande7@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c index 6a4c4cccd643..c45a7ca66ad8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/fs_hws.c @@ -1323,8 +1323,10 @@ mlx5_cmd_hws_packet_reformat_alloc(struct mlx5_flow_root_namespace *ns, break; case MLX5_REFORMAT_TYPE_REMOVE_HDR: hws_action = mlx5_fs_get_action_remove_header_vlan(fs_ctx, params); - if (!hws_action) + if (!hws_action) { mlx5_core_err(dev, "Only vlan remove header supported\n"); + return -EOPNOTSUPP; + } break; default: mlx5_core_err(ns->dev, "Packet-reformat not supported(%d)\n", From fbd0662f9c9a66e8cc3df3099cca8ed6d3837cc7 Mon Sep 17 00:00:00 2001 From: Luka Gejak Date: Sat, 23 May 2026 15:03:30 +0200 Subject: [PATCH 410/697] net: hsr: fix potential OOB access in supervision frame handling [ Upstream commit f229426072fc865654a60978bb7fda790a051ff3 ] Ensure the entire TLV header is linearized before access by adding sizeof(struct hsr_sup_tlv) to the pskb_may_pull() calls. Without this, a truncated frame could cause an out-of-bounds access. Fixes: eafaa88b3eb7 ("net: hsr: Add support for redbox supervision frames") Signed-off-by: Luka Gejak Reviewed-by: Fernando Fernandez Mancera Link: https://patch.msgid.link/20260523130330.61880-1-luka.gejak@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/hsr/hsr_forward.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index aefc9b6936ba..299de290ddaa 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -84,7 +84,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) /* Get next tlv */ total_length += hsr_sup_tag->tlv.HSR_TLV_length; - if (!pskb_may_pull(skb, total_length)) + if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv))) return false; skb_pull(skb, total_length); hsr_sup_tlv = (struct hsr_sup_tlv *)skb->data; @@ -100,7 +100,7 @@ static bool is_supervision_frame(struct hsr_priv *hsr, struct sk_buff *skb) /* make sure another tlv follows */ total_length += sizeof(struct hsr_sup_tlv) + hsr_sup_tlv->HSR_TLV_length; - if (!pskb_may_pull(skb, total_length)) + if (!pskb_may_pull(skb, total_length + sizeof(struct hsr_sup_tlv))) return false; /* get next tlv */ From f2e077e8979f204fd3480d411cea3e7325c596a8 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 26 May 2026 08:35:01 +0200 Subject: [PATCH 411/697] gpio: mxc: fix irq_high handling [ Upstream commit dac917ed5aead741004db8d0d5151dd577802df8 ] If port->irq_high is -1 (fsl,imx21-gpio compatible) and gpio_idx is >= 16 enable_irq_wake() is called with -1 which is wrong. Fixes: 5f6d1998adeb ("gpio: mxc: release the parent IRQ in runtime suspend") Signed-off-by: Alexander Stein Reviewed-by: Frank Li Link: https://patch.msgid.link/20260526063504.25916-1-alexander.stein@ew.tq-group.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-mxc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 441ba95b38cf..dbdf0f41b6bb 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -469,7 +469,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) * the handler is needed only once, but doing it for every port * is more robust and easier. */ - port->irq_high = -1; + port->irq_high = 0; port->mx_irq_handler = mx2_gpio_irq_handler; } else port->mx_irq_handler = mx3_gpio_irq_handler; From a20e6ae5f05e26c4bdf1471d2f96bb622bed09c9 Mon Sep 17 00:00:00 2001 From: Marc Harvey Date: Thu, 9 Apr 2026 02:59:24 +0000 Subject: [PATCH 412/697] net: team: Remove unused team_mode_op, port_enabled [ Upstream commit 014f249121d73909528df320818fba7693d0ec92 ] This team_mode_op wasn't used by any of the team modes, so remove it. Reviewed-by: Jiri Pirko Signed-off-by: Marc Harvey Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260409-teaming-driver-internal-v7-2-f47e7589685d@google.com Signed-off-by: Paolo Abeni Stable-dep-of: 25fe708bbc59 ("net: team: fix NULL pointer dereference in team_xmit during mode change") Signed-off-by: Sasha Levin --- drivers/net/team/team_core.c | 2 -- include/linux/if_team.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index a98f5e506154..712d8043e66e 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -945,8 +945,6 @@ static void team_port_enable(struct team *team, team_port_index_hash(team, port->index)); team_adjust_ops(team); team_queue_override_port_add(team, port); - if (team->ops.port_enabled) - team->ops.port_enabled(team, port); team_notify_peers(team); team_mcast_rejoin(team); team_lower_state_changed(port); diff --git a/include/linux/if_team.h b/include/linux/if_team.h index ce97d891cf72..0d550d44a1c2 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -121,7 +121,6 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_dev_addr)(struct team *team, struct team_port *port); - void (*port_enabled)(struct team *team, struct team_port *port); void (*port_disabled)(struct team *team, struct team_port *port); }; From c2af23b48f936e944bbd779ad12f179f18bd10a5 Mon Sep 17 00:00:00 2001 From: Marc Harvey Date: Thu, 9 Apr 2026 02:59:25 +0000 Subject: [PATCH 413/697] net: team: Rename port_disabled team mode op to port_tx_disabled [ Upstream commit cfa477df2cc62ba53cb936669886361152b594a7 ] This team mode op is only used by the load balance mode, and it only uses it in the tx path. Reviewed-by: Jiri Pirko Signed-off-by: Marc Harvey Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260409-teaming-driver-internal-v7-3-f47e7589685d@google.com Signed-off-by: Paolo Abeni Stable-dep-of: 25fe708bbc59 ("net: team: fix NULL pointer dereference in team_xmit during mode change") Signed-off-by: Sasha Levin --- drivers/net/team/team_core.c | 4 ++-- drivers/net/team/team_mode_loadbalance.c | 4 ++-- include/linux/if_team.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 712d8043e66e..11c8e6551dd3 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -969,8 +969,8 @@ static void team_port_disable(struct team *team, { if (!team_port_enabled(port)) return; - if (team->ops.port_disabled) - team->ops.port_disabled(team, port); + if (team->ops.port_tx_disabled) + team->ops.port_tx_disabled(team, port); hlist_del_rcu(&port->hlist); __reconstruct_port_hlist(team, port->index); port->index = -1; diff --git a/drivers/net/team/team_mode_loadbalance.c b/drivers/net/team/team_mode_loadbalance.c index b14538bde2f8..b27e44a4df5f 100644 --- a/drivers/net/team/team_mode_loadbalance.c +++ b/drivers/net/team/team_mode_loadbalance.c @@ -655,7 +655,7 @@ static void lb_port_leave(struct team *team, struct team_port *port) free_percpu(lb_port_priv->pcpu_stats); } -static void lb_port_disabled(struct team *team, struct team_port *port) +static void lb_port_tx_disabled(struct team *team, struct team_port *port) { lb_tx_hash_to_port_mapping_null_port(team, port); } @@ -665,7 +665,7 @@ static const struct team_mode_ops lb_mode_ops = { .exit = lb_exit, .port_enter = lb_port_enter, .port_leave = lb_port_leave, - .port_disabled = lb_port_disabled, + .port_tx_disabled = lb_port_tx_disabled, .receive = lb_receive, .transmit = lb_transmit, }; diff --git a/include/linux/if_team.h b/include/linux/if_team.h index 0d550d44a1c2..cd5acf40040d 100644 --- a/include/linux/if_team.h +++ b/include/linux/if_team.h @@ -121,7 +121,7 @@ struct team_mode_ops { int (*port_enter)(struct team *team, struct team_port *port); void (*port_leave)(struct team *team, struct team_port *port); void (*port_change_dev_addr)(struct team *team, struct team_port *port); - void (*port_disabled)(struct team *team, struct team_port *port); + void (*port_tx_disabled)(struct team *team, struct team_port *port); }; extern int team_modeop_port_enter(struct team *team, struct team_port *port); From 03e9405c518c4d61f28079492f252d6d4e2bac5c Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Thu, 21 May 2026 01:12:01 -0700 Subject: [PATCH 414/697] net: team: fix NULL pointer dereference in team_xmit during mode change [ Upstream commit 25fe708bbc59289d3d1ea4b126fbc1b460a072a5 ] __team_change_mode() clears team->ops with memset() before restoring safe dummy handlers via team_adjust_ops(). A concurrent team_xmit() running under RCU on another CPU can read team->ops.transmit during this window and call a NULL function pointer, crashing the kernel. The race requires a mode change (CAP_NET_ADMIN) concurrent with transmit on the team device. BUG: kernel NULL pointer dereference, address: 0000000000000000 Oops: 0010 [#1] SMP KASAN NOPTI RIP: 0010:0x0 Call Trace: team_xmit (drivers/net/team/team_core.c:1853) dev_hard_start_xmit (net/core/dev.c:3904) __dev_queue_xmit (net/core/dev.c:4871) packet_sendmsg (net/packet/af_packet.c:3109) __sys_sendto (net/socket.c:2265) The original code assumed that no ports means no traffic, so mode changes could freely memset()/memcpy() the ops. AF_PACKET with forced carrier breaks that assumption. Prevent the race instead of making it safe: replace memset()/memcpy() with per-field updates that never touch transmit or receive. Those two handlers are managed solely by team_adjust_ops(), which already installs dummies when tx_en_port_count == 0 (always true during mode change since no ports are present). WRITE_ONCE/READ_ONCE prevent store/load tearing on the handler pointers. synchronize_net() before exit_op() drains in-flight readers that may still reference old mode state from before port removal switched the handlers to dummies. Fixes: 3d249d4ca7d0 ("net: introduce ethernet teaming device") Reported-by: Xiang Mei Signed-off-by: Weiming Shi Reviewed-by: Jiayuan Chen Link: https://patch.msgid.link/20260521081159.1491563-3-bestswngs@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/team/team_core.c | 45 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c index 11c8e6551dd3..ff4dcd6035cd 100644 --- a/drivers/net/team/team_core.c +++ b/drivers/net/team/team_core.c @@ -535,21 +535,23 @@ static void team_adjust_ops(struct team *team) if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->transmit) - team->ops.transmit = team_dummy_transmit; + WRITE_ONCE(team->ops.transmit, team_dummy_transmit); else - team->ops.transmit = team->mode->ops->transmit; + WRITE_ONCE(team->ops.transmit, team->mode->ops->transmit); if (!team->en_port_count || !team_is_mode_set(team) || !team->mode->ops->receive) - team->ops.receive = team_dummy_receive; + WRITE_ONCE(team->ops.receive, team_dummy_receive); else - team->ops.receive = team->mode->ops->receive; + WRITE_ONCE(team->ops.receive, team->mode->ops->receive); } /* - * We can benefit from the fact that it's ensured no port is present - * at the time of mode change. Therefore no packets are in fly so there's no - * need to set mode operations in any special way. + * team_change_mode() ensures no ports are present during mode change, + * but lockless readers can still reach team_xmit(). Avoid touching + * transmit/receive -- they are already set to dummies by + * team_adjust_ops() since no ports are enabled. synchronize_net() + * drains in-flight readers before destroying old mode state. */ static int __team_change_mode(struct team *team, const struct team_mode *new_mode) @@ -558,9 +560,21 @@ static int __team_change_mode(struct team *team, if (team_is_mode_set(team)) { void (*exit_op)(struct team *team) = team->ops.exit; - /* Clear ops area so no callback is called any longer */ - memset(&team->ops, 0, sizeof(struct team_mode_ops)); - team_adjust_ops(team); + /* Clear cold-path ops used only under RTNL. transmit and + * receive are already dummies (no ports) so leave them + * alone -- overwriting them is the source of the race. + */ + team->ops.init = NULL; + team->ops.exit = NULL; + team->ops.port_enter = NULL; + team->ops.port_leave = NULL; + team->ops.port_change_dev_addr = NULL; + team->ops.port_tx_disabled = NULL; + + /* Wait for in-flight readers before tearing down mode + * state they may reference. + */ + synchronize_net(); if (exit_op) exit_op(team); @@ -583,7 +597,12 @@ static int __team_change_mode(struct team *team, } team->mode = new_mode; - memcpy(&team->ops, new_mode->ops, sizeof(struct team_mode_ops)); + team->ops.init = new_mode->ops->init; + team->ops.exit = new_mode->ops->exit; + team->ops.port_enter = new_mode->ops->port_enter; + team->ops.port_leave = new_mode->ops->port_leave; + team->ops.port_change_dev_addr = new_mode->ops->port_change_dev_addr; + team->ops.port_tx_disabled = new_mode->ops->port_tx_disabled; team_adjust_ops(team); return 0; @@ -744,7 +763,7 @@ static rx_handler_result_t team_handle_frame(struct sk_buff **pskb) /* allow exact match delivery for disabled ports */ res = RX_HANDLER_EXACT; } else { - res = team->ops.receive(team, port, skb); + res = READ_ONCE(team->ops.receive)(team, port, skb); } if (res == RX_HANDLER_ANOTHER) { struct team_pcpu_stats *pcpu_stats; @@ -1683,7 +1702,7 @@ static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev) tx_success = team_queue_override_transmit(team, skb); if (!tx_success) - tx_success = team->ops.transmit(team, skb); + tx_success = READ_ONCE(team->ops.transmit)(team, skb); if (tx_success) { struct team_pcpu_stats *pcpu_stats; From 48fd840a26d35cca8a1c338c22f790db953a64a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= Date: Fri, 22 May 2026 14:06:40 +0200 Subject: [PATCH 415/697] net: Avoid checksumming unreadable skb tail on trim MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 2e357f002c61fd76fd8f12468744a06a5ec48eaa ] pskb_trim_rcsum_slow() keeps CHECKSUM_COMPLETE valid by subtracting the checksum of the bytes removed from the skb tail. That assumes the removed bytes can be read. io_uring zcrx skbs may contain unreadable net_iov frags. With fbnic header/data split, small TCP/IPv4 packets can carry Ethernet padding in such a frag. ip_rcv_core() trims the skb to iph->tot_len before TCP sees it, and the CHECKSUM_COMPLETE adjustment then calls skb_checksum() on the padding. This is exposed by IPv4 because small TCP/IPv4 frames can be shorter than the Ethernet minimum payload. TCP/IPv6 frames are large enough in the normal zcrx path, so they do not hit the same padding trim. Keep the existing checksum adjustment for readable skbs. If the remaining packet is fully linear, drop CHECKSUM_COMPLETE and let the stack validate the packet after trimming. If unreadable payload would remain, fail the trim; the checksum cannot be adjusted without reading the trimmed tail. Also clear skb->unreadable when trimming removes all frags. Fixes: 65249feb6b3d ("net: add support for skbs with unreadable frags") Signed-off-by: Björn Töpel Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260522120643.242974-1-bjorn@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/core/skbuff.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a8911f1b90c1..6618bfa70ca4 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2739,6 +2739,8 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len) skb->data_len = 0; skb_set_tail_pointer(skb, len); } + if (!skb_shinfo(skb)->nr_frags && !skb_has_frag_list(skb)) + skb->unreadable = 0; if (!skb->sk || skb->destructor == sock_edemux) skb_condense(skb); @@ -2746,16 +2748,37 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len) } EXPORT_SYMBOL(___pskb_trim); +static int pskb_trim_rcsum_complete(struct sk_buff *skb, unsigned int len) +{ + int delta = skb->len - len; + + if (skb_frags_readable(skb)) { + skb->csum = csum_block_sub(skb->csum, + skb_checksum(skb, len, delta, 0), + len); + return 0; + } + + if (len > skb_headlen(skb)) + return -EFAULT; + + /* The trimmed bytes are unreadable, but the remaining packet can be + * checksummed by software after trimming. + */ + skb->ip_summed = CHECKSUM_NONE; + return 0; +} + /* Note : use pskb_trim_rcsum() instead of calling this directly */ int pskb_trim_rcsum_slow(struct sk_buff *skb, unsigned int len) { if (skb->ip_summed == CHECKSUM_COMPLETE) { - int delta = skb->len - len; + int err; - skb->csum = csum_block_sub(skb->csum, - skb_checksum(skb, len, delta, 0), - len); + err = pskb_trim_rcsum_complete(skb, len); + if (err) + return err; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { int hdlen = (len > skb_headlen(skb)) ? skb_headlen(skb) : len; int offset = skb_checksum_start_offset(skb) + skb->csum_offset; From f23e4d7324b8d3bd892f52323b0b88d08c8a38e4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:42 -0700 Subject: [PATCH 416/697] ethtool: rss: avoid modifying the RSS context response [ Upstream commit c75b6f6eaacd0b74b832414cc3b9289c3686e941 ] Gemini says that we're modifying the RSS_CREATE response skb. I think it's right, the comment says that unicast() should unshare the skb but I'm not entirely sure what I meant there. netlink_trim() does a copy but only if skb is not well sized (it's at least 2x larger than necessary for the payload). Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink") Link: https://patch.msgid.link/20260522230647.1705600-2-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index da5934cceb07..926be5698ba4 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -974,11 +974,17 @@ ethnl_rss_create_validate(struct net_device *dev, struct genl_info *info) } static void -ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev) +ethnl_rss_create_send_ntf(const struct sk_buff *rsp, struct net_device *dev) { - struct nlmsghdr *nlh = (void *)rsp->data; struct genlmsghdr *genl_hdr; + struct nlmsghdr *nlh; + struct sk_buff *ntf; + + ntf = skb_copy_expand(rsp, 0, 0, GFP_KERNEL); + if (!ntf) + return; + nlh = nlmsg_hdr(ntf); /* Convert the reply into a notification */ nlh->nlmsg_pid = 0; nlh->nlmsg_seq = ethnl_bcast_seq_next(); @@ -986,7 +992,7 @@ ethnl_rss_create_send_ntf(struct sk_buff *rsp, struct net_device *dev) genl_hdr = nlmsg_data(nlh); genl_hdr->cmd = ETHTOOL_MSG_RSS_CREATE_NTF; - ethnl_multicast(rsp, dev); + ethnl_multicast(ntf, dev); } int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) @@ -1094,12 +1100,8 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) genlmsg_end(rsp, hdr); - /* Use the same skb for the response and the notification, - * genlmsg_reply() will copy the skb if it has elevated user count. - */ - skb_get(rsp); - ret = genlmsg_reply(rsp, info); ethnl_rss_create_send_ntf(rsp, dev); + ret = genlmsg_reply(rsp, info); rsp = NULL; exit_unlock: From 6a775ec73210d222dec7f5f08caf1b315e527b8b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:43 -0700 Subject: [PATCH 417/697] ethtool: rss: add missing errno on RSS context delete [ Upstream commit 3e6c6e9782ff8a8d8ded774b07ad4590cd61d04c ] Remember to set ret before jumping out if someone tries to delete a context on a device which doesn't support contexts. Fixes: fbe09277fa63 ("ethtool: rss: support removing contexts via Netlink") Link: https://patch.msgid.link/20260522230647.1705600-3-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 926be5698ba4..688c0e4bba69 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -1160,8 +1160,10 @@ int ethnl_rss_delete_doit(struct sk_buff *skb, struct genl_info *info) dev = req.dev; ops = dev->ethtool_ops; - if (!ops->create_rxfh_context) + if (!ops->create_rxfh_context) { + ret = -EOPNOTSUPP; goto exit_free_dev; + } rtnl_lock(); netdev_lock_ops(dev); From 39c01c405063a8a8697f156dcbde8fcf011e98cd Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:44 -0700 Subject: [PATCH 418/697] ethtool: rss: fix falsely ignoring indir table updates [ Upstream commit 8d60141a32875248ef71d49c9920fa5e2aa40b29 ] rss_set_prep_indir() compares the new indirection table against the current one to determine whether any update is needed. The memcmp call passes data->indir_size as the length argument, but indir_size is the number of u32 entries, not the byte count. Fixes: c0ae03588bbb ("ethtool: rss: initial RSS_SET (indirection table handling)") Link: https://patch.msgid.link/20260522230647.1705600-4-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 688c0e4bba69..4877655f7244 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -684,7 +684,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info, ethtool_rxfh_indir_default(i, num_rx_rings); } - *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size); + *mod |= memcmp(rxfh->indir, data->indir_table, alloc_size); return 0; From 33d05c22d6f227c5ae171c46df2f6f8bf48047ea Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:45 -0700 Subject: [PATCH 419/697] ethtool: rss: fix indir_table and hkey leak on get_rxfh failure [ Upstream commit 266297692f97008ca48bc311775c087c59bd7fe3 ] rss_prepare_get() allocates the indirection table and hash key buffer via rss_get_data_alloc(), then calls ops->get_rxfh() to populate them. If get_rxfh() fails, the function returns an error without freeing the allocation. Fixes: 4f038a6a02d2 ("net: ethtool: Don't call .cleanup_data when prepare_data fails") Link: https://patch.msgid.link/20260522230647.1705600-5-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 4877655f7244..5416aec13b7f 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -168,8 +168,10 @@ rss_prepare_get(const struct rss_req_info *request, struct net_device *dev, rxfh.key = data->hkey; ret = ops->get_rxfh(dev, &rxfh); - if (ret) + if (ret) { + rss_get_data_free(data); goto out_unlock; + } data->hfunc = rxfh.hfunc; data->input_xfrm = rxfh.input_xfrm; From 7ddc3b3ddee8c78e2d82f6cfa682a201faafdc4d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:46 -0700 Subject: [PATCH 420/697] ethtool: rss: fix hkey leak when indir_size is 0 [ Upstream commit 78ccf1a70c6378e1f5073a8c2209b5129067b925 ] rss_get_data_alloc() allocates a single buffer that backs both the indirection table and the hash key, but only assigned data->indir_table when indir_size was nonzero. The expectation was that no driver implements RSS without supporting indirection table but apparently enic does just that (it's the only such in-tree driver). enic has get_rxfh_key_size but no get_rxfh_indir_size. data->indir_table stays as NULL, hkey gets set but rss_get_data_free() kfree(data->indir_table) is a nop and the allocation leaks. Always store the allocation base in data->indir_table so the free path is unambiguous. No caller treats indir_table as a sentinel; everything keys off indir_size. Fixes: 7112a04664bf ("ethtool: add netlink based get rss support") Link: https://patch.msgid.link/20260522230647.1705600-6-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 5416aec13b7f..f745ddec6fba 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -132,8 +132,7 @@ rss_get_data_alloc(struct net_device *dev, struct rss_reply_data *data) if (!rss_config) return -ENOMEM; - if (data->indir_size) - data->indir_table = (u32 *)rss_config; + data->indir_table = (u32 *)rss_config; if (data->hkey_size) data->hkey = rss_config + indir_bytes; From 7877d8fbbec2d03d4dd890728f9777e1679fa731 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:06:47 -0700 Subject: [PATCH 421/697] ethtool: rss: avoid device context leak on reply-build failure [ Upstream commit 32a9ecde62731c9f7412507709192c84dafc38d1 ] We wait with filling the reply for new RSS context creation until after the driver ->create_rxfh_context call. The driver needs to fill some of the defaults in the context. The failure of rss_fill_reply() is somewhat theoretical, but doesn't take much effort to handle it properly. Call ->remove_rxfh_context(). If the driver's remove callback fails (some implementations like sfc can return real command errors from firmware RPCs) - skip the xa_erase and kfree, leaving the context in the xarray. This matches how ethnl_rss_delete_doit() behaves. Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink") Link: https://patch.msgid.link/20260522230647.1705600-7-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/rss.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index f745ddec6fba..b122f67dbde1 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -1096,7 +1096,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) ntf_fail |= rss_fill_reply(rsp, &req.base, &data.base); if (WARN_ON(!hdr || ntf_fail)) { ret = -EMSGSIZE; - goto exit_unlock; + goto err_remove_ctx; } genlmsg_end(rsp, hdr); @@ -1124,6 +1124,10 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info) nlmsg_free(rsp); return ret; +err_remove_ctx: + if (ops->remove_rxfh_context(dev, ctx, req.rss_context, NULL)) + /* leave the context on failure, like ethnl_rss_delete_doit() */ + goto exit_unlock; err_ctx_id_free: xa_erase(&dev->ethtool->rss_ctx, req.rss_context); err_unlock_free_ctx: From d9defbf8b62bde89e206d74c2a2b445b9ed66108 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:04 -0700 Subject: [PATCH 422/697] ethtool: module: call ethnl_ops_complete() on module flash errors [ Upstream commit 84371fb58423f997939aacdcbc02d128d76a54e5 ] When validate() fails we are skipping over ethnl_ops_complete() even tho we already called ethnl_ops_begin(). Fixes: 32b4c8b53ee7 ("ethtool: Add ability to flash transceiver modules' firmware") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-2-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/module.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/ethtool/module.c b/net/ethtool/module.c index 4d4e0a82579a..05e4c1d78565 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -427,10 +427,11 @@ int ethnl_act_module_fw_flash(struct sk_buff *skb, struct genl_info *info) ret = ethnl_module_fw_flash_validate(dev, info->extack); if (ret < 0) - goto out_unlock; + goto out_complete; ret = module_flash_fw(dev, tb, skb, info); +out_complete: ethnl_ops_complete(dev); out_unlock: From 61848c83b9132ab839809fe415ba7802a0aca4f6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:05 -0700 Subject: [PATCH 423/697] ethtool: module: avoid leaking a netdev ref on module flash errors [ Upstream commit fb7f511d62692661846c47f199e0afe25c2982db ] module_flash_fw_schedule() is missing undo for setting the "in_progress" flag and taking the netdev reference. Delay taking these, the device can't disappear while we are holding rtnl_lock. Fixes: 32b4c8b53ee7 ("ethtool: Add ability to flash transceiver modules' firmware") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-3-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/module.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/ethtool/module.c b/net/ethtool/module.c index 05e4c1d78565..fb61bb47083e 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -319,8 +319,6 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name, if (err < 0) goto err_release_firmware; - dev->ethtool->module_fw_flash_in_progress = true; - netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL); fw_update->dev = dev; fw_update->ntf_params.portid = info->snd_portid; fw_update->ntf_params.seq = info->snd_seq; @@ -335,6 +333,9 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name, if (err < 0) goto err_release_firmware; + dev->ethtool->module_fw_flash_in_progress = true; + netdev_hold(dev, &module_fw->dev_tracker, GFP_KERNEL); + schedule_work(&module_fw->work); return 0; From 9f5108f5ee27302437db855abbc36fb0deb7e4e8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:06 -0700 Subject: [PATCH 424/697] ethtool: module: avoid racy updates to dev->ethtool bitfield [ Upstream commit 7a84b965ffc12030af63cd10a8f3a1123ff39b7a ] When reviewing other changes Gemini points out that we currently update module_fw_flash_in_progress without holding any locks. Since module_fw_flash_in_progress is part of a bitfield this is not great, updates to other fields may be lost. We could use a bool and sprinkle some READ_ONCE/WRITE_ONCE here but seems like the issue is rather than the work is an unusual writer. The other writers already hold the right locks. So just very briefly take these locks when the work completes. Note that nothing ever cancels the FW update work, so there's no concern with deadlocks vs cancel. Fixes: 32b4c8b53ee7 ("ethtool: Add ability to flash transceiver modules' firmware") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-4-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/module.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/ethtool/module.c b/net/ethtool/module.c index fb61bb47083e..8929b7fb2fa7 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -221,14 +221,22 @@ static void module_flash_fw_work_list_del(struct list_head *list) static void module_flash_fw_work(struct work_struct *work) { struct ethtool_module_fw_flash *module_fw; + struct net_device *dev; module_fw = container_of(work, struct ethtool_module_fw_flash, work); + dev = module_fw->fw_update.dev; ethtool_cmis_fw_update(&module_fw->fw_update); module_flash_fw_work_list_del(&module_fw->list); - module_fw->fw_update.dev->ethtool->module_fw_flash_in_progress = false; - netdev_put(module_fw->fw_update.dev, &module_fw->dev_tracker); + + rtnl_lock(); + netdev_lock_ops(dev); + dev->ethtool->module_fw_flash_in_progress = false; + netdev_unlock_ops(dev); + rtnl_unlock(); + + netdev_put(dev, &module_fw->dev_tracker); release_firmware(module_fw->fw_update.fw); kfree(module_fw); } From 9e70c8efb0caf3652864b1c3f867b89082a04580 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:07 -0700 Subject: [PATCH 425/697] ethtool: module: check fw_flash_in_progress under rtnl_lock [ Upstream commit 504eaefa44c8dec50f7499edcb36d24f3aefab2a ] ethnl_set_module_validate() inspects module_fw_flash_in_progress but validate is meant for _input_ validation, not state validation. rtnl_lock is not held, yet. Move the check into ethnl_set_module(). Fixes: 32b4c8b53ee7 ("ethtool: Add ability to flash transceiver modules' firmware") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-5-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/module.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/ethtool/module.c b/net/ethtool/module.c index 8929b7fb2fa7..202e4c25280a 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -120,12 +120,6 @@ ethnl_set_module_validate(struct ethnl_req_info *req_info, if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) return 0; - if (req_info->dev->ethtool->module_fw_flash_in_progress) { - NL_SET_ERR_MSG(info->extack, - "Module firmware flashing is in progress"); - return -EBUSY; - } - if (!ops->get_module_power_mode || !ops->set_module_power_mode) { NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY], @@ -148,6 +142,12 @@ ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info) ops = dev->ethtool_ops; + if (dev->ethtool->module_fw_flash_in_progress) { + NL_SET_ERR_MSG(info->extack, + "Module firmware flashing is in progress"); + return -EBUSY; + } + power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]); ret = ops->get_module_power_mode(dev, &power, info->extack); if (ret < 0) From e1dd697094f1d4223052c82f140fb49b3aaad5bb Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:08 -0700 Subject: [PATCH 426/697] ethtool: module: fix cleanup if socket used for flashing multiple devices [ Upstream commit 760d04ebad5c4304f22c0d2251c9623b87a117c8 ] When a single Netlink socket issues MODULE_FW_FLASH_ACT against multiple devices, ethnl_sock_priv_set() overwrites sk_priv->dev on each call, retaining only the last one. The socket priv is used on socket close, to walk the global work list and mark the uncompleted flashing work as "orphaned". Otherwise if another socket reuses the PID it will unexpectedly receive the flashing notifications. Don't record the device, record net pointer instead. The purpose of the dev is to scope the work to a netns, anyway. If we store netns the overrides are safe/a nop since all flashed devices must be in the same netns as the socket. Fixes: 32b4c8b53ee7 ("ethtool: Add ability to flash transceiver modules' firmware") Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-6-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/module.c | 9 ++++----- net/ethtool/netlink.c | 4 ++-- net/ethtool/netlink.h | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/net/ethtool/module.c b/net/ethtool/module.c index 202e4c25280a..9a11e7def002 100644 --- a/net/ethtool/module.c +++ b/net/ethtool/module.c @@ -291,11 +291,9 @@ void ethnl_module_fw_flash_sock_destroy(struct ethnl_sock_priv *sk_priv) spin_lock(&module_fw_flash_work_list_lock); list_for_each_entry(work, &module_fw_flash_work_list, list) { - if (work->fw_update.dev == sk_priv->dev && - work->fw_update.ntf_params.portid == sk_priv->portid) { + if (work->fw_update.ntf_params.portid == sk_priv->portid && + dev_net(work->fw_update.dev) == sk_priv->net) work->fw_update.ntf_params.closed_sock = true; - break; - } } spin_unlock(&module_fw_flash_work_list_lock); } @@ -332,7 +330,8 @@ module_flash_fw_schedule(struct net_device *dev, const char *file_name, fw_update->ntf_params.seq = info->snd_seq; fw_update->ntf_params.closed_sock = false; - err = ethnl_sock_priv_set(skb, dev, fw_update->ntf_params.portid, + err = ethnl_sock_priv_set(skb, dev_net(dev), + fw_update->ntf_params.portid, ETHTOOL_SOCK_TYPE_MODULE_FW_FLASH); if (err < 0) goto err_release_firmware; diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 2f813f25f07e..28577b878fd5 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -52,7 +52,7 @@ const struct nla_policy ethnl_header_policy_phy_stats[] = { [ETHTOOL_A_HEADER_PHY_INDEX] = NLA_POLICY_MIN(NLA_U32, 1), }; -int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, +int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid, enum ethnl_sock_type type) { struct ethnl_sock_priv *sk_priv; @@ -61,7 +61,7 @@ int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, if (IS_ERR(sk_priv)) return PTR_ERR(sk_priv); - sk_priv->dev = dev; + sk_priv->net = net; sk_priv->portid = portid; sk_priv->type = type; diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 1d4f9ecb3d26..3923188097ac 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -318,12 +318,12 @@ enum ethnl_sock_type { }; struct ethnl_sock_priv { - struct net_device *dev; + struct net *net; u32 portid; enum ethnl_sock_type type; }; -int ethnl_sock_priv_set(struct sk_buff *skb, struct net_device *dev, u32 portid, +int ethnl_sock_priv_set(struct sk_buff *skb, struct net *net, u32 portid, enum ethnl_sock_type type); /** From 4d42fb88ec61f2e98c33a9e3a2de371d5edbc6b1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:09 -0700 Subject: [PATCH 427/697] ethtool: cmis: require exact CDB reply length [ Upstream commit 6c3f999a9d1338c6c89a9ff4549eafe72bc2e7b1 ] Malicious SFP module could respond with rpl_len longer than what cmis_cdb_process_reply() expected, leading to OOB writes. Malicious HW is a bit theoretical but some modules may just be buggy and/or the reads may occasionally get corrupted, so let's protect the kernel. The existing check protects from short replies. We need to protect from long ones, too. All callers that pass a non-zero rpl_exp_len cast the reply payload to a fixed-layout struct and read fields at fixed offsets, with no version negotiation or short-reply handling: - cmis_cdb_validate_password() - cmis_cdb_module_features_get() - cmis_fw_update_fw_mng_features_get() so let's assume that responses longer than expected do not have to be handled gracefully here. Add a warning message to make the debug easier in case my understanding is wrong... Note that page_data->length (argument of kmalloc) comes from last arg to ethtool_cmis_page_init() which is rpl_exp_len. Note2 that AIs also like to point out overflows in args->req.payload itself (which is a fixed-size 120 B buffer, on the stack), but callers should be reading structs defined by the standard, so protecting from requests for more data than max seem like defensive programming. Fixes: a39c84d79625 ("ethtool: cmis_cdb: Add a layer for supporting CDB commands") Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-7-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/cmis_cdb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/ethtool/cmis_cdb.c b/net/ethtool/cmis_cdb.c index 3057576bc81e..fe156991d0be 100644 --- a/net/ethtool/cmis_cdb.c +++ b/net/ethtool/cmis_cdb.c @@ -513,8 +513,13 @@ static int cmis_cdb_process_reply(struct net_device *dev, } rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data; - if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) || - !rpl->hdr.rpl_chk_code) { + if (rpl->hdr.rpl_len != args->rpl_exp_len) { + netdev_warn(dev, "CDB reply length mismatch, expected %u got %u\n", + args->rpl_exp_len, rpl->hdr.rpl_len); + err = -EIO; + goto out; + } + if (!rpl->hdr.rpl_chk_code) { err = -EIO; goto out; } From 0cbce444db75f64dceee839bf8794470e78f8e68 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:10 -0700 Subject: [PATCH 428/697] ethtool: cmis: fix u16-to-u8 truncation of msleep_pre_rpl [ Upstream commit 3e8c3d464c36bb342fe377b026577c7ec27fdbb4 ] ethtool_cmis_cdb_compose_args() accepts msleep_pre_rpl as u16 but stores it into the u8 field ethtool_cmis_cdb_cmd_args::msleep_pre_rpl, silently truncating values >= 256. Seven of the nine call sites pass 1000 ms (it's the third argument from the end). Fixes: a39c84d79625 ("ethtool: cmis_cdb: Add a layer for supporting CDB commands") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-8-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/cmis.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ethtool/cmis.h b/net/ethtool/cmis.h index 4a9a946cabf0..778783a0f23c 100644 --- a/net/ethtool/cmis.h +++ b/net/ethtool/cmis.h @@ -63,9 +63,9 @@ struct ethtool_cmis_cdb_request { * struct ethtool_cmis_cdb_cmd_args - CDB commands execution arguments * @req: CDB command fields as described in the CMIS standard. * @max_duration: Maximum duration time for command completion in msec. + * @msleep_pre_rpl: Waiting time before checking reply in msec. * @read_write_len_ext: Allowable additional number of byte octets to the LPL * in a READ or a WRITE commands. - * @msleep_pre_rpl: Waiting time before checking reply in msec. * @rpl_exp_len: Expected reply length in bytes. * @flags: Validation flags for CDB commands. * @err_msg: Error message to be sent to user space. @@ -73,8 +73,8 @@ struct ethtool_cmis_cdb_request { struct ethtool_cmis_cdb_cmd_args { struct ethtool_cmis_cdb_request req; u16 max_duration; + u16 msleep_pre_rpl; u8 read_write_len_ext; - u8 msleep_pre_rpl; u8 rpl_exp_len; u8 flags; char *err_msg; From 0696709e951be54c699664adf546d16e28974d53 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:11 -0700 Subject: [PATCH 429/697] ethtool: cmis: validate start_cmd_payload_size from module [ Upstream commit 12c2496a71f82f63617971ca9b730dffa05cf58b ] The CMIS firmware update code reads start_cmd_payload_size from the module's FW Management Features CDB reply and uses it directly as the byte count for memcpy. The destination buffer is 112 bytes (ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - 8). So a malicious module (or corrupted response) can cause a OOB write later on in cmis_fw_update_start_download(). Let's error out. If modules that expect longer LPL writes actually exist we should revisit. struct cmis_cdb_start_fw_download_pl's definition has to move, no change there. Fixes: c4f78134d45c ("ethtool: cmis_fw_update: add a layer for supporting firmware update using CDB") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-9-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/cmis_fw_update.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/net/ethtool/cmis_fw_update.c b/net/ethtool/cmis_fw_update.c index df5f344209c4..16190c97e1f7 100644 --- a/net/ethtool/cmis_fw_update.c +++ b/net/ethtool/cmis_fw_update.c @@ -44,6 +44,20 @@ enum cmis_cdb_fw_write_mechanism { CMIS_CDB_FW_WRITE_MECHANISM_BOTH = 0x11, }; +/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard + * revision 5.2. + * struct cmis_cdb_start_fw_download_pl is a structured layout of the + * flat array, ethtool_cmis_cdb_request::payload. + */ +struct cmis_cdb_start_fw_download_pl { + __struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */, + __be32 image_size; + __be32 resv1; + ); + u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - + sizeof(struct cmis_cdb_start_fw_download_pl_h)]; +}; + static int cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb, struct net_device *dev, @@ -86,6 +100,14 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb, */ cdb->read_write_len_ext = rpl->read_write_len_ext; fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size; + if (fw_mng->start_cmd_payload_size > + sizeof_field(struct cmis_cdb_start_fw_download_pl, vendor_data)) { + ethnl_module_fw_flash_ntf_err(dev, ntf_params, + "Start cmd payload size exceeds max LPL payload", + NULL); + return -EINVAL; + } + fw_mng->write_mechanism = rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ? CMIS_CDB_FW_WRITE_MECHANISM_LPL : @@ -97,20 +119,6 @@ cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb, return 0; } -/* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard - * revision 5.2. - * struct cmis_cdb_start_fw_download_pl is a structured layout of the - * flat array, ethtool_cmis_cdb_request::payload. - */ -struct cmis_cdb_start_fw_download_pl { - __struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */, - __be32 image_size; - __be32 resv1; - ); - u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - - sizeof(struct cmis_cdb_start_fw_download_pl_h)]; -}; - static int cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb, struct ethtool_cmis_fw_update_params *fw_update, From 41d2dc766bf8cade44ca25ad10673ad740bb89cc Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 22 May 2026 16:13:12 -0700 Subject: [PATCH 430/697] ethtool: cmis: validate fw->size against start_cmd_payload_size [ Upstream commit d5551f4c1800dc714cec86647bdd651ae0de923e ] cmis_fw_update_start_download() copies start_cmd_payload_size bytes from the firmware blob into the CDB LPL vendor_data[] payload without validating that the FW has enough data. Since the start_cmd_payload_size can only be ~120B an image too short is most likely corrupted, so reject it. Fixes: c4f78134d45c ("ethtool: cmis_fw_update: add a layer for supporting firmware update using CDB") Reviewed-by: Maxime Chevallier Reviewed-by: Danielle Ratson Link: https://patch.msgid.link/20260522231312.1710836-10-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/cmis_fw_update.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/ethtool/cmis_fw_update.c b/net/ethtool/cmis_fw_update.c index 16190c97e1f7..291d04d2776a 100644 --- a/net/ethtool/cmis_fw_update.c +++ b/net/ethtool/cmis_fw_update.c @@ -130,6 +130,14 @@ cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb, u8 lpl_len; int err; + if (fw_update->fw->size < vendor_data_size) { + ethnl_module_fw_flash_ntf_err(fw_update->dev, + &fw_update->ntf_params, + "Firmware image too small for module's start payload", + NULL); + return -EINVAL; + } + pl.image_size = cpu_to_be32(fw_update->fw->size); memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size); From 2a8c9994406b4f1892e85c0dfb8b9cb106084019 Mon Sep 17 00:00:00 2001 From: Li Ming Date: Wed, 20 May 2026 20:14:57 +0800 Subject: [PATCH 431/697] cxl/test: Update mock dev array before calling platform_device_add() [ Upstream commit d90f236f8b9e354848bd226f581db27755ab901d ] CXL test environment hits the following error sometimes. cxl_mem mem9: endpoint7 failed probe All mock memdevs are platform firmware devices added by cxl_test module, and cxl_test module also provides a platform device driver for them to create a memdev device to CXL subsystem. cxl_test module uses cxl_rcd/mem_single/mem arrays to store different types of mock memdevs. CXL drivers calls registered mock functions for a mock memdev by checking if a given memdev is in these arrays. When cxl_test module adds these mock memdevs, it always calls platform_device_add() before adding them to a suitable mock memdev array. However, there is a small window where CXL drivers calls mock function for a added memdev before it added to a mock memdev array. In above case, cxl endpoint driver considers a added memdev was not a mock memdev, then calling devm_cxl_endpoint_decoders_setup() for it rather than mock_endpoint_decoders_setup(). An appropriate solution is that adding a new mock device to a mock device array before calling platform_device_add() for it. It can guarantee the new mock device is visible to CXL subsystem. This patch introduces a new helped called cxl_mock_platform_device_add() to handle the issue, and uses the function for all mock devices addition. Fixes: 3a2b97b3210b ("cxl/test: Improve init-order fidelity relative to real-world systems") Signed-off-by: Li Ming Tested-by: Alison Schofield Reviewed-by: Alison Schofield Link: https://patch.msgid.link/20260520121457.234404-1-ming.li@zohomail.com Signed-off-by: Dave Jiang Signed-off-by: Sasha Levin --- tools/testing/cxl/test/cxl.c | 105 ++++++++++++++--------------------- 1 file changed, 43 insertions(+), 62 deletions(-) diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c index 2d135ca533d0..5771c75bc588 100644 --- a/tools/testing/cxl/test/cxl.c +++ b/tools/testing/cxl/test/cxl.c @@ -1132,6 +1132,23 @@ static void mock_companion(struct acpi_device *adev, struct device *dev) #define SZ_64G (SZ_32G * 2) #endif +static int cxl_mock_platform_device_add(struct platform_device *pdev, + struct platform_device **ppdev) +{ + int rc; + + if (ppdev) + *ppdev = pdev; + rc = platform_device_add(pdev); + if (rc) { + platform_device_put(pdev); + if (ppdev) + *ppdev = NULL; + } + + return rc; +} + static __init int cxl_rch_topo_init(void) { int rc, i; @@ -1146,13 +1163,10 @@ static __init int cxl_rch_topo_init(void) goto err_bridge; mock_companion(adev, &pdev->dev); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_rch[i]); + if (rc) goto err_bridge; - } - cxl_rch[i] = pdev; mock_pci_bus[idx].bridge = &pdev->dev; rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj, "firmware_node"); @@ -1204,13 +1218,10 @@ static __init int cxl_single_topo_init(void) goto err_bridge; mock_companion(adev, &pdev->dev); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_hb_single[i]); + if (rc) goto err_bridge; - } - cxl_hb_single[i] = pdev; mock_pci_bus[i + NR_CXL_HOST_BRIDGES].bridge = &pdev->dev; rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj, "physical_node"); @@ -1229,12 +1240,9 @@ static __init int cxl_single_topo_init(void) goto err_port; pdev->dev.parent = &bridge->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_root_single[i]); + if (rc) goto err_port; - } - cxl_root_single[i] = pdev; } for (i = 0; i < ARRAY_SIZE(cxl_swu_single); i++) { @@ -1247,12 +1255,9 @@ static __init int cxl_single_topo_init(void) goto err_uport; pdev->dev.parent = &root_port->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_swu_single[i]); + if (rc) goto err_uport; - } - cxl_swu_single[i] = pdev; } for (i = 0; i < ARRAY_SIZE(cxl_swd_single); i++) { @@ -1266,12 +1271,9 @@ static __init int cxl_single_topo_init(void) goto err_dport; pdev->dev.parent = &uport->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_swd_single[i]); + if (rc) goto err_dport; - } - cxl_swd_single[i] = pdev; } return 0; @@ -1344,12 +1346,9 @@ static int cxl_mem_init(void) pdev->dev.parent = &dport->dev; set_dev_node(&pdev->dev, i % 2); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_mem[i]); + if (rc) goto err_mem; - } - cxl_mem[i] = pdev; } for (i = 0; i < ARRAY_SIZE(cxl_mem_single); i++) { @@ -1362,12 +1361,9 @@ static int cxl_mem_init(void) pdev->dev.parent = &dport->dev; set_dev_node(&pdev->dev, i % 2); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_mem_single[i]); + if (rc) goto err_single; - } - cxl_mem_single[i] = pdev; } for (i = 0; i < ARRAY_SIZE(cxl_rcd); i++) { @@ -1381,12 +1377,9 @@ static int cxl_mem_init(void) pdev->dev.parent = &rch->dev; set_dev_node(&pdev->dev, i % 2); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_rcd[i]); + if (rc) goto err_rcd; - } - cxl_rcd[i] = pdev; } return 0; @@ -1451,13 +1444,10 @@ static __init int cxl_test_init(void) goto err_bridge; mock_companion(adev, &pdev->dev); - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_host_bridge[i]); + if (rc) goto err_bridge; - } - cxl_host_bridge[i] = pdev; mock_pci_bus[i].bridge = &pdev->dev; rc = sysfs_create_link(&pdev->dev.kobj, &pdev->dev.kobj, "physical_node"); @@ -1475,12 +1465,9 @@ static __init int cxl_test_init(void) goto err_port; pdev->dev.parent = &bridge->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_root_port[i]); + if (rc) goto err_port; - } - cxl_root_port[i] = pdev; } BUILD_BUG_ON(ARRAY_SIZE(cxl_switch_uport) != ARRAY_SIZE(cxl_root_port)); @@ -1493,12 +1480,9 @@ static __init int cxl_test_init(void) goto err_uport; pdev->dev.parent = &root_port->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_switch_uport[i]); + if (rc) goto err_uport; - } - cxl_switch_uport[i] = pdev; } for (i = 0; i < ARRAY_SIZE(cxl_switch_dport); i++) { @@ -1511,12 +1495,9 @@ static __init int cxl_test_init(void) goto err_dport; pdev->dev.parent = &uport->dev; - rc = platform_device_add(pdev); - if (rc) { - platform_device_put(pdev); + rc = cxl_mock_platform_device_add(pdev, &cxl_switch_dport[i]); + if (rc) goto err_dport; - } - cxl_switch_dport[i] = pdev; } rc = cxl_single_topo_init(); @@ -1534,9 +1515,9 @@ static __init int cxl_test_init(void) mock_companion(&acpi0017_mock, &cxl_acpi->dev); acpi0017_mock.dev.bus = &platform_bus_type; - rc = platform_device_add(cxl_acpi); + rc = cxl_mock_platform_device_add(cxl_acpi, NULL); if (rc) - goto err_root; + goto err_rch; rc = cxl_mem_init(); if (rc) From 6dff77899b9e9fe5d854abda3a98ad04e7229ef7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 25 May 2026 20:13:35 +0000 Subject: [PATCH 432/697] tunnels: load network headers after skb_cow() in iptunnel_pmtud_build_icmp[v6]() [ Upstream commit b4bc94353050b1fa7b702bd4c6600710dd926cff ] Sashiko found that iptunnel_pmtud_build_icmp() and iptunnel_pmtud_build_icmpv6() were caching ip_hdr() and ipv6_hdr() before an skb_cow() call which can reallocate skb->head. Fix this possible UAF by initializing the local variables after the skb_cow() call. Remove skb_reset_network_header() calls which were not needed. Fixes: 4cb47a8644cc ("tunnels: PMTU discovery support for directly bridged IP packets") Signed-off-by: Eric Dumazet Reviewed-by: Stefano Brivio Link: https://patch.msgid.link/20260525201335.2361845-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/ip_tunnel_core.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index 5683c328990f..d5a63155a055 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -212,7 +212,7 @@ EXPORT_SYMBOL_GPL(iptunnel_handle_offloads); */ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) { - const struct iphdr *iph = ip_hdr(skb); + const struct iphdr *iph; struct icmphdr *icmph; struct iphdr *niph; struct ethhdr eh; @@ -226,7 +226,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); pskb_pull(skb, ETH_HLEN); - skb_reset_network_header(skb); err = pskb_trim(skb, 576 - sizeof(*niph) - sizeof(*icmph)); if (err) @@ -236,7 +235,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) err = skb_cow(skb, sizeof(*niph) + sizeof(*icmph) + ETH_HLEN); if (err) return err; - + iph = ip_hdr(skb); icmph = skb_push(skb, sizeof(*icmph)); *icmph = (struct icmphdr) { .type = ICMP_DEST_UNREACH, @@ -308,7 +307,7 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu) */ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) { - const struct ipv6hdr *ip6h = ipv6_hdr(skb); + const struct ipv6hdr *ip6h; struct icmp6hdr *icmp6h; struct ipv6hdr *nip6h; struct ethhdr eh; @@ -323,7 +322,6 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) skb_copy_bits(skb, skb_mac_offset(skb), &eh, ETH_HLEN); pskb_pull(skb, ETH_HLEN); - skb_reset_network_header(skb); err = pskb_trim(skb, IPV6_MIN_MTU - sizeof(*nip6h) - sizeof(*icmp6h)); if (err) @@ -334,6 +332,7 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu) if (err) return err; + ip6h = ipv6_hdr(skb); icmp6h = skb_push(skb, sizeof(*icmp6h)); *icmp6h = (struct icmp6hdr) { .icmp6_type = ICMPV6_PKT_TOOBIG, From 5303925e360527243b46a440a04667826bbc72b7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 25 May 2026 20:36:42 +0000 Subject: [PATCH 433/697] vxlan: do not reuse cached ip_hdr() value after skb_tunnel_check_pmtu() [ Upstream commit 7d9ef0cb271555d8cf39fefe6c981e1493b25ecf ] skb_tunnel_check_pmtu() can change skb->head. Reusing old_iph afer skb_tunnel_check_pmtu() can cause an UAF. Use instead ip_hdr(skb) as done in drivers/net/bareudp.c and drivers/net/geneve.c. Found by Sashiko. Fixes: 4cb47a8644cc ("tunnels: PMTU discovery support for directly bridged IP packets") Signed-off-by: Eric Dumazet Reviewed-by: Stefano Brivio Link: https://patch.msgid.link/20260525203642.2389723-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/vxlan/vxlan_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c index d2d0e0bd4371..a4ff66e354f5 100644 --- a/drivers/net/vxlan/vxlan_core.c +++ b/drivers/net/vxlan/vxlan_core.c @@ -2532,7 +2532,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto out_unlock; } - tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb); ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); err = vxlan_build_skb(skb, ndst, sizeof(struct iphdr), vni, md, flags, udp_sum); @@ -2606,7 +2606,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, goto out_unlock; } - tos = ip_tunnel_ecn_encap(tos, old_iph, skb); + tos = ip_tunnel_ecn_encap(tos, ip_hdr(skb), skb); ttl = ttl ? : ip6_dst_hoplimit(ndst); skb_scrub_packet(skb, xnet); err = vxlan_build_skb(skb, ndst, sizeof(struct ipv6hdr), From 43368636c663cff6e59dde93cf4b8e43ac28eb93 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 22 May 2026 11:55:12 +0000 Subject: [PATCH 434/697] tunnels: do not assume transport header in iptunnel_pmtud_check_icmp() [ Upstream commit 509323077ef79a26ba0c60bb556e45c12c398b2d ] In some cases, iptunnel_pmtud_check_icmp() can be called while skb transport header is not set. This triggers an out-of-bound access, because (typeof(skb->transport_header))~0U is 65535. Access the icmp header based on IPv4 network header, after making sure icmp->type is present in skb linear part. Note that iptunnel_pmtud_check_icmpv6()) is fine. Fixes: 4cb47a8644cc ("tunnels: PMTU discovery support for directly bridged IP packets") Reported-by: Damiano Melotti Signed-off-by: Eric Dumazet Reviewed-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260522115512.1519110-1-edumazet@google.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv4/ip_tunnel_core.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index d5a63155a055..4b5fd4b13722 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -280,7 +280,6 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu) */ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu) { - const struct icmphdr *icmph = icmp_hdr(skb); const struct iphdr *iph = ip_hdr(skb); if (mtu < 576 || iph->frag_off != htons(IP_DF)) @@ -291,9 +290,17 @@ static int iptunnel_pmtud_check_icmp(struct sk_buff *skb, int mtu) ipv4_is_lbcast(iph->saddr) || ipv4_is_multicast(iph->saddr)) return 0; - if (iph->protocol == IPPROTO_ICMP && icmp_is_err(icmph->type)) - return 0; + if (iph->protocol == IPPROTO_ICMP) { + const struct icmphdr *icmph; + if (!pskb_network_may_pull(skb, iph->ihl * 4 + + offsetofend(struct icmphdr, type))) + return 0; + iph = ip_hdr(skb); + icmph = (void *)iph + iph->ihl * 4; + if (icmp_is_err(icmph->type)) + return 0; + } return iptunnel_pmtud_build_icmp(skb, mtu); } From de9eb0b44fa9123170e6245b49638e0e453c10f8 Mon Sep 17 00:00:00 2001 From: Sean Shen Date: Tue, 26 May 2026 22:07:16 +0900 Subject: [PATCH 435/697] ksmbd: fix FSCTL permission bypass by adding a permission check for FSCTL_SET_SPARSE [ Upstream commit cc57232cae23c0df91b4a59d0f519141ce9b5b02 ] FSCTL_SET_SPARSE in fsctl_set_sparse() modifies the file's sparse attribute and saves it through xattr without any permission checks. This exposes two issues: 1) A client on a read-only share can change the sparse attribute on files it opened, even though the share is read-only. Other FSCTL write operations already check test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE), but FSCTL_SET_SPARSE does not. 2) Even on writable shares, clients without FILE_WRITE_DATA or FILE_WRITE_ATTRIBUTES access should not modify the sparse attribute. Similar handle-level checks exist in other functions but are missing here. Add both share-level writable check and per-handle access check. Use goto out on error to avoid leaking file references. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: Namjae Jeon Cc: Sergey Senozhatsky Cc: Steve French Signed-off-by: Sean Shen Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/server/smb2pdu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index da7b96707186..4689aac12c14 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -8203,9 +8203,20 @@ static inline int fsctl_set_sparse(struct ksmbd_work *work, u64 id, int ret = 0; __le32 old_fattr; + if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) { + ksmbd_debug(SMB, "User does not have write permission\n"); + return -EACCES; + } + fp = ksmbd_lookup_fd_fast(work, id); if (!fp) return -ENOENT; + + if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_WRITE_ATTRIBUTES_LE))) { + ret = -EACCES; + goto out; + } + idmap = file_mnt_idmap(fp->filp); old_fattr = fp->f_ci->m_fattr; From 164dcbec9632ca93ae313e6da6e4e05584fa0f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Wed, 27 May 2026 09:24:00 -0300 Subject: [PATCH 436/697] ASoC: codecs: simple-mux: Fix enum control bounds check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f63ad68e18d774a5d15cd7e405ead63f6b322679 ] simple_mux_control_put() rejects values greater than e->items, but enum control values are zero based. For the two-entry mux used by this driver, valid values are 0 and 1, so value 2 must be rejected as well. Accepting e->items can store an invalid mux state, pass it to the GPIO setter, and pass it on to the DAPM mux update path where it is used as an index into the enum text array. Use the same >= e->items check used by the ASoC enum helpers. Fixes: 342fbb7578d1 ("ASoC: add simple-mux") Signed-off-by: Cássio Gabriel Link: https://patch.msgid.link/20260527-asoc-simple-mux-enum-bounds-v1-1-3f805b9fc671@gmail.com Signed-off-by: Mark Brown Signed-off-by: Sasha Levin --- sound/soc/codecs/simple-mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c index 390696440155..cedd181ffdaf 100644 --- a/sound/soc/codecs/simple-mux.c +++ b/sound/soc/codecs/simple-mux.c @@ -51,7 +51,7 @@ static int simple_mux_control_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *c = snd_soc_dapm_to_component(dapm); struct simple_mux *priv = snd_soc_component_get_drvdata(c); - if (ucontrol->value.enumerated.item[0] > e->items) + if (ucontrol->value.enumerated.item[0] >= e->items) return -EINVAL; if (priv->mux == ucontrol->value.enumerated.item[0]) From 75cf24709037c671c62c49cd12285893bd679eb2 Mon Sep 17 00:00:00 2001 From: Balasubramani Vivekanandan Date: Fri, 22 May 2026 22:05:32 +0530 Subject: [PATCH 437/697] drm/xe: Restore IDLEDLY regiter on engine reset [ Upstream commit f657a6a3ba4c20bc01f5be3752d53498ee1bfe35 ] Wa_16023105232 programs the register IDLEDLY. The register is reset whenever the engine is reset. Therefore it should be added to the GuC save-restore register list for it to be restored after reset. Fixes: 7c53ff050ba8 ("drm/xe: Apply Wa_16023105232") Reviewed-by: Matt Roper Link: https://patch.msgid.link/20260522163531.1365540-2-balasubramani.vivekanandan@intel.com Signed-off-by: Balasubramani Vivekanandan (cherry picked from commit df1cfe24743a93b71eab27687e148ab8ae9b69e3) Signed-off-by: Rodrigo Vivi Signed-off-by: Sasha Levin --- drivers/gpu/drm/xe/xe_guc_ads.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c index 0e2bece1d8b8..db71823b2538 100644 --- a/drivers/gpu/drm/xe/xe_guc_ads.c +++ b/drivers/gpu/drm/xe/xe_guc_ads.c @@ -772,6 +772,11 @@ static unsigned int guc_mmio_regset_write(struct xe_guc_ads *ads, } } + if (XE_GT_WA(hwe->gt, 16023105232)) + guc_mmio_regset_write_one(ads, regset_map, + RING_IDLEDLY(hwe->mmio_base), + count++); + return count; } From e673889a35a5e4c586d0fae67d8755ca4367d3e2 Mon Sep 17 00:00:00 2001 From: Zhao Dongdong Date: Tue, 26 May 2026 11:21:39 +0800 Subject: [PATCH 438/697] Bluetooth: 6lowpan: check skb_clone() return value in send_mcast_pkt() [ Upstream commit 3c40d381ce04f9575a5d8b542898183c3b4b38dc ] The skb_clone() function can return NULL if memory allocation fails. send_mcast_pkt() calls skb_clone() without checking the return value, which can lead to a NULL pointer dereference in send_pkt() when it dereferences skb->data. Add a NULL check after skb_clone() and skip the peer if the clone fails. Fixes: 18722c247023 ("Bluetooth: Enable 6LoWPAN support for BT LE devices") Signed-off-by: Zhao Dongdong Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/6lowpan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 2c21ae8abadc..038f01600eeb 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -486,6 +486,8 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) int ret; local_skb = skb_clone(skb, GFP_ATOMIC); + if (!local_skb) + continue; BT_DBG("xmit %s to %pMR type %u IP %pI6c chan %p", netdev->name, From 9ea8a648d9120f7652bcde1ce2c4ad66871af707 Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 26 May 2026 21:33:19 +0200 Subject: [PATCH 439/697] bonding: refuse to enslave CAN devices [ Upstream commit 8ba68464e4787b6a7ec938826e16124df20fd23d ] syzbot reported a kernel paging request crash in can_rx_unregister() inside net/can/af_can.c. The crash occurs because a virtual CAN device (vxcan) is being enslaved to a bonding master. During the enslavement process, the bonding driver mutates and modifies the network device states to fit an Ethernet-like aggregation model. However, CAN devices operate on a completely different Layer 2 architecture, relying on the CAN mid-layer private data structure (can_ml_priv) instead of standard Ethernet structures. Since bonding does not initialize or maintain these CAN structures, subsequent operations on the half-enslaved interface (such as closing associated sockets via isotp_release) lead to a null-pointer dereference when accessing the CAN receiver lists. Bonding CAN interfaces is architecturally invalid as CAN lacks MAC addresses, ARP capabilities, and standard Ethernet link-layer mechanisms. While generic loopback devices are blocked globally in net/core/dev.c, virtual CAN devices bypass this check because they do not carry the IFF_LOOPBACK flag, despite acting as local software-loopbacks. Fix this by explicitly blocking network devices of type ARPHRD_CAN from being enslaved at the very beginning of bond_enslave(). This prevents illegal state mutations, eliminates the resulting KASAN crashes, and avoids potential memory leaks from incomplete socket cleanups. As the CAN support has been added a long time after bonding the Fixes-tag points to the introduction of ARPHRD_CAN that would have needed a specific handling in bonding_main.c. Fixes: cd05acfe65ed ("[CAN]: Allocate protocol numbers for PF_CAN") Reported-by: syzbot+8ed98cbd0161632bce95@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=8ed98cbd0161632bce95 Signed-off-by: Oliver Hartkopp Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260526-bonding-candev-v1-1-ba1df400918a@hartkopp.net Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/bonding/bond_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8b1422dda4c0..2132acff2e52 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1860,6 +1860,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, struct sockaddr_storage ss; int res = 0, i; + if (slave_dev->type == ARPHRD_CAN) { + BOND_NL_ERR(bond_dev, extack, + "CAN devices cannot be enslaved"); + return -EPERM; + } + if (slave_dev->flags & IFF_MASTER && !netif_is_bond_master(slave_dev)) { BOND_NL_ERR(bond_dev, extack, From c9c2e609e8397bb57b4d73675f33a99183c4a0bd Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 26 May 2026 09:48:16 +0300 Subject: [PATCH 440/697] bridge: Fix sleep in atomic context in netlink path [ Upstream commit 5eec4427b89c2fb2beac54920101e55a2f1c0c21 ] Since the introduction of the netlink configuration path for bridge ports in commit 25c71c75ac87 ("bridge: bridge port parameters over netlink"), br_setport() was always called with the bridge lock held around it. Back then this decision made sense: The bridge lock protects the STP state of the bridge and its ports and at that time the function only processed three STP related netlink attributes (cost, priority and state). Nowadays, br_setport() processes a lot more attributes and most of them do not need the bridge lock: * Bridge flags: Only require RTNL. Read locklessly by the data path. Annotations can be added in net-next. * FDB port flushing: Only requires the FDB lock. * Multicast attributes: Only require the multicast lock. * Group forward mask: Only requires RTNL. Read locklessly by the data path. Annotations can be added in net-next. * Backup port and NHID: Only require RTNL. Read locklessly by the data path. This is a problem as the bridge calls dev_set_promiscuity() when certain bridge port flags change and this function can sleep since the commit cited below, resulting in a splat such as [1]. Fix this by reducing the scope of the bridge lock and only take it when processing the three STP related attributes that require it. This is consistent with the multicast attributes where each attribute acquires the multicast lock instead of having one critical section for all relevant attributes. [1] BUG: sleeping function called from invalid context at net/core/dev_addr_lists.c:1262 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 356, name: bridge preempt_count: 201, expected: 0 RCU nest depth: 0, expected: 0 2 locks held by bridge/356: #0: ffffffff919473a0 (rtnl_mutex){+.+.}-{4:4}, at: rtnetlink_rcv_msg (net/core/rtnetlink.c:80 net/core/rtnetlink.c:7002) #1: ffff888115072d58 (&br->lock){+...}-{3:3}, at: br_setlink (./include/linux/spinlock.h:348 net/bridge/br_netlink.c:1117) Preemption disabled at: 0x0 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) __might_resched.cold (kernel/sched/core.c:9163) netif_rx_mode_run (net/core/dev_addr_lists.c:1262) netif_rx_mode_sync (net/core/dev_addr_lists.c:1428) dev_set_promiscuity (net/core/dev_api.c:289) br_manage_promisc (net/bridge/br_if.c:135 net/bridge/br_if.c:172) br_port_flags_change (net/bridge/br_if.c:242 net/bridge/br_if.c:747) br_setport (net/bridge/br_netlink.c:1000) br_setlink (net/bridge/br_netlink.c:1118) rtnl_bridge_setlink (net/core/rtnetlink.c:5572) rtnetlink_rcv_msg (net/core/rtnetlink.c:7005) netlink_rcv_skb (net/netlink/af_netlink.c:2550) netlink_unicast (net/netlink/af_netlink.c:1318 net/netlink/af_netlink.c:1344) netlink_sendmsg (net/netlink/af_netlink.c:1894) __sock_sendmsg (net/socket.c:787 (discriminator 4) net/socket.c:802 (discriminator 4)) ____sys_sendmsg (net/socket.c:2698) ___sys_sendmsg (net/socket.c:2752) __sys_sendmsg (net/socket.c:2784) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Fixes: 78cd408356fe ("net: add missing instance lock to dev_set_promiscuity") Reviewed-by: Nikolay Aleksandrov Signed-off-by: Ido Schimmel Link: https://patch.msgid.link/20260526064818.272516-2-idosch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_netlink.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 4e2d53b27221..6d5b2ef5f18d 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -1000,19 +1000,25 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[], br_port_flags_change(p, changed_mask); if (tb[IFLA_BRPORT_COST]) { + spin_lock_bh(&p->br->lock); err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); + spin_unlock_bh(&p->br->lock); if (err) return err; } if (tb[IFLA_BRPORT_PRIORITY]) { + spin_lock_bh(&p->br->lock); err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY])); + spin_unlock_bh(&p->br->lock); if (err) return err; } if (tb[IFLA_BRPORT_STATE]) { + spin_lock_bh(&p->br->lock); err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE])); + spin_unlock_bh(&p->br->lock); if (err) return err; } @@ -1114,9 +1120,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags, if (err) return err; - spin_lock_bh(&p->br->lock); err = br_setport(p, tb, extack); - spin_unlock_bh(&p->br->lock); } else { /* Binary compatibility with old RSTP */ if (nla_len(protinfo) < sizeof(u8)) @@ -1203,17 +1207,10 @@ static int br_port_slave_changelink(struct net_device *brdev, struct nlattr *data[], struct netlink_ext_ack *extack) { - struct net_bridge *br = netdev_priv(brdev); - int ret; - if (!data) return 0; - spin_lock_bh(&br->lock); - ret = br_setport(br_port_get_rtnl(dev), data, extack); - spin_unlock_bh(&br->lock); - - return ret; + return br_setport(br_port_get_rtnl(dev), data, extack); } static int br_port_fill_slave_info(struct sk_buff *skb, From e976e3f2f2005c6267089a1a3b6344f234a59a55 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 26 May 2026 09:48:17 +0300 Subject: [PATCH 441/697] bridge: Fix sleep in atomic context in sysfs path [ Upstream commit 6d34594cc619d0d4b07d5afcad8b5984f3526dcf ] Since the start of the git history, brport_store() always acquired the bridge lock. Back then this decision made sense: The bridge lock protects the STP state of the bridge and its ports and at that time the function was only used by two STP related attributes (cost and priority). Nowadays, brport_store() processes a lot more attributes and most of them do not need the bridge lock: * Bridge flags: Only require RTNL. Read locklessly by the data path. Annotations can be added in net-next. * FDB port flushing: Only requires the FDB lock. * Multicast attributes: Only require the multicast lock. * Group forward mask: Only requires RTNL. Read locklessly by the data path. Annotations can be added in net-next. * Backup port: Only requires RTNL. Read locklessly by the data path. This is a problem as the bridge calls dev_set_promiscuity() when certain bridge port flags change and this function can sleep since the commit cited below, resulting in a splat such as [1]. Fix this by reducing the scope of the bridge lock and only take it when processing the two STP related attributes that require it. Remove the now stale comment from br_switchdev_set_port_flag(). The SWITCHDEV_F_DEFER flag can be removed in net-next. [1] BUG: sleeping function called from invalid context at net/core/dev_addr_lists.c:1262 in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 372, name: bash preempt_count: 201, expected: 0 RCU nest depth: 0, expected: 0 5 locks held by bash/372: #0: ffff88810c51c3f0 (sb_writers#7){.+.+}-{0:0}, at: ksys_write (fs/read_write.c:740) #1: ffff888115ce9480 (&of->mutex){+.+.}-{4:4}, at: kernfs_fop_write_iter (fs/kernfs/file.c:343) #2: ffff88810b9fd330 (kn->active#37){.+.+}-{0:0}, at: kernfs_fop_write_iter (fs/kernfs/file.c:80 fs/kernfs/file.c:344) #3: ffffffffa59473a0 (rtnl_mutex){+.+.}-{4:4}, at: brport_store (net/bridge/br_sysfs_if.c:326) #4: ffff8881099d2d58 (&br->lock){+...}-{3:3}, at: brport_store (./include/linux/spinlock.h:348 net/bridge/br_sysfs_if.c:345) Preemption disabled at: 0x0 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Call Trace: dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) __might_resched.cold (kernel/sched/core.c:9163) netif_rx_mode_run (net/core/dev_addr_lists.c:1262) netif_rx_mode_sync (net/core/dev_addr_lists.c:1428) dev_set_promiscuity (net/core/dev_api.c:289) br_manage_promisc (net/bridge/br_if.c:135 net/bridge/br_if.c:172) br_port_flags_change (net/bridge/br_if.c:242 net/bridge/br_if.c:747) store_learning (net/bridge/br_sysfs_if.c:79 net/bridge/br_sysfs_if.c:235) brport_store (net/bridge/br_sysfs_if.c:346) kernfs_fop_write_iter (fs/kernfs/file.c:352) new_sync_write (fs/read_write.c:595) vfs_write (fs/read_write.c:688) ksys_write (fs/read_write.c:740) do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) Fixes: 78cd408356fe ("net: add missing instance lock to dev_set_promiscuity") Reviewed-by: Nikolay Aleksandrov Signed-off-by: Ido Schimmel Link: https://patch.msgid.link/20260526064818.272516-3-idosch@nvidia.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/bridge/br_switchdev.c | 1 - net/bridge/br_sysfs_if.c | 30 ++++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c index fe3f7bbe86ee..b5c6e314204f 100644 --- a/net/bridge/br_switchdev.c +++ b/net/bridge/br_switchdev.c @@ -99,7 +99,6 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, attr.u.brport_flags.val = flags; attr.u.brport_flags.mask = mask; - /* We run from atomic context here */ err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev, &info.info, extack); err = notifier_to_errno(err); diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 74fdd8105dca..3fe664fd1f5b 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -86,16 +86,34 @@ static ssize_t show_path_cost(struct net_bridge_port *p, char *buf) return sprintf(buf, "%d\n", p->path_cost); } -static BRPORT_ATTR(path_cost, 0644, - show_path_cost, br_stp_set_path_cost); +static int store_path_cost(struct net_bridge_port *p, unsigned long v) +{ + int ret; + + spin_lock_bh(&p->br->lock); + ret = br_stp_set_path_cost(p, v); + spin_unlock_bh(&p->br->lock); + return ret; +} + +static BRPORT_ATTR(path_cost, 0644, show_path_cost, store_path_cost); static ssize_t show_priority(struct net_bridge_port *p, char *buf) { return sprintf(buf, "%d\n", p->priority); } -static BRPORT_ATTR(priority, 0644, - show_priority, br_stp_set_port_priority); +static int store_priority(struct net_bridge_port *p, unsigned long v) +{ + int ret; + + spin_lock_bh(&p->br->lock); + ret = br_stp_set_port_priority(p, v); + spin_unlock_bh(&p->br->lock); + return ret; +} + +static BRPORT_ATTR(priority, 0644, show_priority, store_priority); static ssize_t show_designated_root(struct net_bridge_port *p, char *buf) { @@ -334,17 +352,13 @@ static ssize_t brport_store(struct kobject *kobj, ret = -ENOMEM; goto out_unlock; } - spin_lock_bh(&p->br->lock); ret = brport_attr->store_raw(p, buf_copy); - spin_unlock_bh(&p->br->lock); kfree(buf_copy); } else if (brport_attr->store) { val = simple_strtoul(buf, &endp, 0); if (endp == buf) goto out_unlock; - spin_lock_bh(&p->br->lock); ret = brport_attr->store(p, val); - spin_unlock_bh(&p->br->lock); } if (!ret) { From 0c02c190bcd9822477038ff2cee10ea584ac1b1d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:24 -0700 Subject: [PATCH 442/697] ethtool: coalesce: cap profile updates at NET_DIM_PARAMS_NUM_PROFILES [ Upstream commit 7281b096b072f6c6e30420e3467d738f2e4c4b57 ] ethnl_update_profile() walks the ETHTOOL_A_PROFILE_IRQ_MODERATION nest list with an index 'i' and writes new_profile[i++] without bounding i. The destination is kmemdup()'d at NET_DIM_PARAMS_NUM_PROFILES entries (5), but the Netlink nest count is entirely user-controlled. Netlink policies do not have support for constraining the number of nested entries (or number of multi-attr entries). Fixes: f750dfe825b9 ("ethtool: provide customized dim profile management") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-2-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/coalesce.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c index 3e18ca1ccc5e..cace02d964cb 100644 --- a/net/ethtool/coalesce.c +++ b/net/ethtool/coalesce.c @@ -463,6 +463,12 @@ static int ethnl_update_profile(struct net_device *dev, nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION, nests, rem) { + if (i >= NET_DIM_PARAMS_NUM_PROFILES) { + NL_SET_BAD_ATTR(extack, nest); + ret = -E2BIG; + goto err_out; + } + ret = nla_parse_nested(tb, len_irq_moder - 1, nest, coalesce_irq_moderation_policy, extack); From d11c98484485bead8de86d315052752a343f22d0 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:25 -0700 Subject: [PATCH 443/697] ethtool: tsconfig: fix reply error handling [ Upstream commit a888bbd43940cada72f7686337741ce86d1cf869 ] A couple of trivial bugs in error handling in tsconfig_send_reply(). If we failed to allocate rskb we need to set the error. If we did allocate it but failed to send it - we need to remember to free it. Fixes: 6e9e2eed4f39 ("net: ethtool: Add support for tsconfig command to get/set hwtstamp config") Reviewed-by: Vadim Fedorenko Reviewed-by: Kory Maincent Link: https://patch.msgid.link/20260526153533.2779187-3-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/tsconfig.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/ethtool/tsconfig.c b/net/ethtool/tsconfig.c index 169b413b31fc..041de8687472 100644 --- a/net/ethtool/tsconfig.c +++ b/net/ethtool/tsconfig.c @@ -224,16 +224,21 @@ static int tsconfig_send_reply(struct net_device *dev, struct genl_info *info) reply_len = ret + ethnl_reply_header_size(); rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_MSG_TSCONFIG_SET_REPLY, ETHTOOL_A_TSCONFIG_HEADER, info, &reply_payload); - if (!rskb) + if (!rskb) { + ret = -ENOMEM; goto err_cleanup; + } ret = tsconfig_fill_reply(rskb, &req_info->base, &reply_data->base); if (ret < 0) - goto err_cleanup; + goto err_free_msg; genlmsg_end(rskb, reply_payload); ret = genlmsg_reply(rskb, info); + rskb = NULL; +err_free_msg: + nlmsg_free(rskb); err_cleanup: kfree(reply_data); kfree(req_info); From 49455e27838aa0da04ab18ac529ec9d9edb460cd Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:26 -0700 Subject: [PATCH 444/697] ethtool: linkstate: fix unbalanced ethnl_ops_complete() on PHY lookup error [ Upstream commit 596c51ed9e125b12c4d85b4530dfd4c7847634b7 ] linkstate_prepare_data() calls ethnl_req_get_phydev() before ethnl_ops_begin(), but routes its error path through "goto out" which calls ethnl_ops_complete(). Fixes: fe55b1d401c6 ("ethtool: linkstate: migrate linkstate functions to support multi-PHY setups") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-4-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/linkstate.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c index 05a5f72c99fa..3dc52a39d345 100644 --- a/net/ethtool/linkstate.c +++ b/net/ethtool/linkstate.c @@ -105,10 +105,8 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base, phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_LINKSTATE_HEADER, info->extack); - if (IS_ERR(phydev)) { - ret = PTR_ERR(phydev); - goto out; - } + if (IS_ERR(phydev)) + return PTR_ERR(phydev); ret = ethnl_ops_begin(dev); if (ret < 0) From 912f8b23bc4b6c76e4378999e490fc360ba8fcf4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:27 -0700 Subject: [PATCH 445/697] ethtool: pse-pd: fix missing ethnl_ops_complete() [ Upstream commit ab5bf428fb6bd361163c7247b92750d1d24ca2ed ] pse_prepare_data() is missing ethnl_ops_complete() if ethnl_req_get_phydev() returned an error. Move getting phydev up so that we don't have to worry about this (similar order to linkstate_prepare_data()). Note that phydev may still be NULL (this is checked in pse_get_pse_attributes()), the goal isn't really to avoid the _begin() / _complete() calls, only to simplify the error handling. While at it propagate the original error. Why this code overrides the error with -ENODEV but !phydev generates -EOPNOTSUPP is unclear to me... Fixes: 31748765bed3 ("net: ethtool: pse-pd: Target the command to the requested PHY") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-5-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/pse-pd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c index 24def9c9dd54..aa4514333d13 100644 --- a/net/ethtool/pse-pd.c +++ b/net/ethtool/pse-pd.c @@ -61,14 +61,14 @@ static int pse_prepare_data(const struct ethnl_req_info *req_base, struct phy_device *phydev; int ret; - ret = ethnl_ops_begin(dev); - if (ret < 0) - return ret; - phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_PSE_HEADER, info->extack); if (IS_ERR(phydev)) - return -ENODEV; + return PTR_ERR(phydev); + + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; ret = pse_get_pse_attributes(phydev, info->extack, data); From d02342d9bb4f0ab682f1846a4fbc15dd2955f7e6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:28 -0700 Subject: [PATCH 446/697] ethtool: tsconfig: fix missing ethnl_ops_complete() [ Upstream commit 6386bd772de64e6760306eb91c7e86163af6c22f ] tsconfig_prepare_data() calls ethnl_ops_begin(), we need to call ethnl_ops_complete() before returning the error. Fixes: 6e9e2eed4f39 ("net: ethtool: Add support for tsconfig command to get/set hwtstamp config") Reviewed-by: Vadim Fedorenko Reviewed-by: Kory Maincent Link: https://patch.msgid.link/20260526153533.2779187-6-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/tsconfig.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ethtool/tsconfig.c b/net/ethtool/tsconfig.c index 041de8687472..a121928038b0 100644 --- a/net/ethtool/tsconfig.c +++ b/net/ethtool/tsconfig.c @@ -69,8 +69,10 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base, if (ret) goto out; - if (ts_info.phc_index == -1) - return -ENODEV; + if (ts_info.phc_index == -1) { + ret = -ENODEV; + goto out; + } data->hwprov_desc.index = ts_info.phc_index; data->hwprov_desc.qualifier = ts_info.phc_qualifier; From ab94e0d6664ddd3e227cc23049b6e52148cb67d8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:29 -0700 Subject: [PATCH 447/697] ethtool: tsinfo: fix uninitialized stats on the by-PHC path [ Upstream commit 1de405699c62c3a9544bcdcfb9eff8a01cfc7582 ] tsinfo_prepare_data() has two code paths: a "by-PHC" path for user-specified hardware timestamping providers, and the old path. Commit 89e281ebff72 ("ethtool: init tsinfo stats if requested") added ethtool_stats_init() to mark stat slots as ETHTOOL_STAT_NOT_SET before the driver callback populates them, but placed the call inside the old-path block. When commit b9e3f7dc9ed9 ("net: ethtool: tsinfo: Enhance tsinfo to support several hwtstamp by net topology") added the by-PHC early return, it landed above the stats initialization. On that path the stats array retains the zero-fill from ethnl_init_reply_data()'s zalloc. This leads to the reply including a stats nest with four zero-valued attributes that should have been absent. Reject GET requests for stats with HWTSTAMP_PROVIDER or dump. Fixes: b9e3f7dc9ed9 ("net: ethtool: tsinfo: Enhance tsinfo to support several hwtstamp by net topology") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-7-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/tsinfo.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index 8c654caa6805..fcc28fca526d 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -81,6 +81,11 @@ tsinfo_parse_request(struct ethnl_req_info *req_base, struct nlattr **tb, if (!tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER]) return 0; + if (req_base->flags & ETHTOOL_FLAG_STATS) { + NL_SET_ERR_MSG(extack, "can't query statistics for a provider"); + return -EOPNOTSUPP; + } + return ts_parse_hwtst_provider(tb[ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER], &req->hwprov_desc, extack, &mod); } @@ -521,6 +526,12 @@ int ethnl_tsinfo_start(struct netlink_callback *cb) if (ret < 0) goto free_reply_data; + if (req_info->base.flags & ETHTOOL_FLAG_STATS) { + NL_SET_ERR_MSG(cb->extack, "stats not supported in dump"); + ret = -EOPNOTSUPP; + goto err_dev_put; + } + ctx->req_info = req_info; ctx->reply_data = reply_data; ctx->pos_ifindex = 0; @@ -530,6 +541,8 @@ int ethnl_tsinfo_start(struct netlink_callback *cb) return 0; +err_dev_put: + ethnl_parse_header_dev_put(&req_info->base); free_reply_data: kfree(reply_data); free_req_info: From 2008f9bb1ede9b688624a241228b8e54fc74f0f6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:30 -0700 Subject: [PATCH 448/697] ethtool: tsinfo: don't pass ERR_PTR to genlmsg_cancel on prepare failure [ Upstream commit c3fc9976f686f9a95baf87db9d387f218fd65394 ] The goto err label leads to: genlmsg_cancel(skb, ehdr); return ret; If ethnl_tsinfo_prepare_dump() failed, it has not started a genlmsg. There's nothing to cancel, and passing an error pointer to genlmsg_cancel() would cause a crash. Fixes: b9e3f7dc9ed9 ("net: ethtool: tsinfo: Enhance tsinfo to support several hwtstamp by net topology") Reviewed-by: Maxime Chevallier Reviewed-by: Kory Maincent Link: https://patch.msgid.link/20260526153533.2779187-8-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/tsinfo.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/ethtool/tsinfo.c b/net/ethtool/tsinfo.c index fcc28fca526d..53f12128a1a5 100644 --- a/net/ethtool/tsinfo.c +++ b/net/ethtool/tsinfo.c @@ -405,10 +405,8 @@ static int ethnl_tsinfo_dump_one_netdev(struct sk_buff *skb, continue; ehdr = ethnl_tsinfo_prepare_dump(skb, dev, reply_data, cb); - if (IS_ERR(ehdr)) { - ret = PTR_ERR(ehdr); - goto err; - } + if (IS_ERR(ehdr)) + return PTR_ERR(ehdr); reply_data->ts_info.phc_qualifier = ctx->pos_phcqualifier; ret = ops->get_ts_info(dev, &reply_data->ts_info); From 3e656023a64965ccbad417131e82b4e2ba3ad070 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:31 -0700 Subject: [PATCH 449/697] ethtool: strset: fix header attribute index in ethnl_req_get_phydev() [ Upstream commit a8d8bef6b45bf7cc0b1f6110c5cd8d0160a9bad7 ] strset_prepare_data() passes ETHTOOL_A_HEADER_FLAGS (3) as the header attribute to ethnl_req_get_phydev(). This is incorrect, in the main attr space 3 is ETHTOOL_A_STRSET_COUNTS_ONLY, not the request header attr. The correct constant is ETHTOOL_A_STRSET_HEADER (1). ethnl_req_get_phydev() only uses this value for the extack, so this is not a "functionally visible"(?) bug. Fixes: e96c93aa4be9 ("net: ethtool: strset: Allow querying phy stats by index") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-9-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/strset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c index f6a67109beda..872ca593b976 100644 --- a/net/ethtool/strset.c +++ b/net/ethtool/strset.c @@ -309,7 +309,7 @@ static int strset_prepare_data(const struct ethnl_req_info *req_base, return 0; } - phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS, + phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_STRSET_HEADER, info->extack); /* phydev can be NULL, check for errors only */ From c944cab3df82567a9560fefc87728782d2ccf69e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:32 -0700 Subject: [PATCH 450/697] ethtool: eeprom: add missing ethnl_ops_begin() / _complete() during fallback [ Upstream commit 2376586f85f972fefe701f095bb37dcfe7405d21 ] All ethtool driver op calls should be sandwiched between ethnl_ops_begin() / ethnl_ops_complete(). In Netlink eeprom code, if the paged access failed we fall back to old API, but we first call _complete() and the fallback never does its own ethnl_ops_begin(). Move the fallback into the _begin() / _complete() section. Fixes: 96d971e307cc ("ethtool: Add fallback to get_module_eeprom from netlink command") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-10-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/eeprom.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ethtool/eeprom.c b/net/ethtool/eeprom.c index 3b8209e930fd..03cb418a1582 100644 --- a/net/ethtool/eeprom.c +++ b/net/ethtool/eeprom.c @@ -140,12 +140,11 @@ static int eeprom_prepare_data(const struct ethnl_req_info *req_base, return 0; err_ops: + if (ret == -EOPNOTSUPP) + ret = eeprom_fallback(request, reply); ethnl_ops_complete(dev); err_free: kfree(page_data.data); - - if (ret == -EOPNOTSUPP) - return eeprom_fallback(request, reply); return ret; } From fd0de51c54fa8474a0ddeedd71c65ad09fada390 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 26 May 2026 08:35:33 -0700 Subject: [PATCH 451/697] ethtool: eeprom: add more safeties to EEPROM Netlink fallback [ Upstream commit 67cfdd9210b99f260b3e0afeb9525e0acc7be31e ] The Netlink fallback path for reading module EEPROM (fallback_set_params()) validates that offset < eeprom_len, but does not check that offset + length stays within eeprom_len. The ioctl equivalent (ethtool_get_any_eeprom() in ioctl.c) has always enforced both bounds: if (eeprom.offset + eeprom.len > total_len) return -EINVAL; This could lead to surprises in both drivers and device FW. Add the missing offset + length validation to fallback_set_params(), mirroring the ioctl. Similarly - ethtool core in general, and ethtool_get_any_eeprom() in particular tries to zero-init all buffers passed to the drivers to avoid any extra work of zeroing things out. eeprom_fallback() uses a plain kmalloc(), change it to zalloc. Fixes: 96d971e307cc ("ethtool: Add fallback to get_module_eeprom from netlink command") Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260526153533.2779187-11-kuba@kernel.org Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ethtool/eeprom.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ethtool/eeprom.c b/net/ethtool/eeprom.c index 03cb418a1582..80af38a6c76a 100644 --- a/net/ethtool/eeprom.c +++ b/net/ethtool/eeprom.c @@ -43,6 +43,9 @@ static int fallback_set_params(struct eeprom_req_info *request, if (offset >= modinfo->eeprom_len) return -EINVAL; + if (length > modinfo->eeprom_len - offset) + return -EINVAL; + eeprom->cmd = ETHTOOL_GMODULEEEPROM; eeprom->len = length; eeprom->offset = offset; @@ -68,7 +71,7 @@ static int eeprom_fallback(struct eeprom_req_info *request, if (err < 0) return err; - data = kmalloc(eeprom.len, GFP_KERNEL); + data = kzalloc(eeprom.len, GFP_KERNEL); if (!data) return -ENOMEM; err = ethtool_get_module_eeprom_call(dev, &eeprom, data); From 6fe1cb312038516cb4d9fa089d700af7059f1a64 Mon Sep 17 00:00:00 2001 From: Rahul Chandelkar Date: Mon, 25 May 2026 21:10:31 +0530 Subject: [PATCH 452/697] ipv6: rpl: fix hdrlen overflow in ipv6_rpl_srh_decompress() [ Upstream commit 9d5e7a46a9f6d8f503b41bfefef70659845f1679 ] ipv6_rpl_srh_decompress() computes: outhdr->hdrlen = (((n + 1) * sizeof(struct in6_addr)) >> 3); hdrlen is __u8. For n >= 127 the result exceeds 255 and silently truncates. With n=127 (cmpri=15, cmpre=15, pad=0, hdrlen=16): (128 * 16) >> 3 = 256, truncated to 0 as __u8 The caller in ipv6_rpl_srh_rcv() then places the compressed header at buf + ((ohdr->hdrlen + 1) << 3). With hdrlen=0 this is buf + 8, but the decompressed region occupies buf[0..2055] (8-byte header plus 128 full addresses). The compressed header overlaps the decompressed data, and ipv6_rpl_srh_compress() writes into this overlap, corrupting the routing header of the forwarded packet. The existing guard at exthdrs.c:546 checks (n + 1) > 255, which prevents n+1 from overflowing unsigned char (the segments_left field), but does not prevent the computed hdrlen from overflowing __u8. n=127 passes because 128 <= 255, yet hdrlen=256 does not fit. Tighten the bound to (n + 1) > 127. This caps n at 126, giving hdrlen = (127 * 16) >> 3 = 254, which fits in __u8. The compressed header then lands at buf + ((254 + 1) << 3) = buf + 2040, exactly past the decompressed region (buf[0..2039]). No overlap. 127 segments is well beyond any realistic RPL deployment. Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr") Signed-off-by: Rahul Chandelkar Link: https://patch.msgid.link/20260525154031.2290876-1-rc@rexion.ai Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/exthdrs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index d15e60943820..830131b427f0 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -544,7 +544,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) * unsigned char which is segments_left field. Should not be * higher than that. */ - if (r || (n + 1) > 255) { + if (r || (n + 1) > 127) { kfree_skb(skb); return -1; } From 7f97b8352ce55c87bc4227d5a16ef0f997e27329 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Mon, 25 May 2026 08:25:49 -0400 Subject: [PATCH 453/697] net/sched: Revert "net/sched: Restrict conditions for adding duplicating netems to qdisc tree" [ Upstream commit eda0b7f203bb166c98d1418b204135bd566ac83b ] This reverts commit ec8e0e3d7adef940cdf9475e2352c0680189d14e. The original patch rejects any tree containing two netems when either has duplication set, even when they sit on unrelated classes of the same classful parent. That broke configurations that have worked since netem was introduced. The re-entrancy problem the original commit was trying to solve is handled by later patch using tc_depth flag. Doing this revert will (re)expose the original bug with multiple netem duplication. When this patch is backported make sure and get the full series. Fixes: ec8e0e3d7ade ("net/sched: Restrict conditions for adding duplicating netems to qdisc tree") Reported-by: Ji-Soo Chung Reported-by: Gerlinde Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220774 Reported-by: zyc zyc Closes: https://lore.kernel.org/all/19adda5a1e2.12410b78222774.9191120410578703463@zohomail.cn/ Reported-by: Manas Ghandat Closes: https://lore.kernel.org/netdev/f69b2c8f-8325-4c2e-a011-6dbc089f30e4@gmail.com/ Reviewed-by: Stephen Hemminger Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260525122556.973584-3-jhs@mojatatu.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/sched/sch_netem.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 47db6da905c5..73b3a8ce2f43 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -1006,41 +1006,6 @@ static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, return 0; } -static const struct Qdisc_class_ops netem_class_ops; - -static int check_netem_in_tree(struct Qdisc *sch, bool duplicates, - struct netlink_ext_ack *extack) -{ - struct Qdisc *root, *q; - unsigned int i; - - root = qdisc_root_sleeping(sch); - - if (sch != root && root->ops->cl_ops == &netem_class_ops) { - if (duplicates || - ((struct netem_sched_data *)qdisc_priv(root))->duplicate) - goto err; - } - - if (!qdisc_dev(root)) - return 0; - - hash_for_each(qdisc_dev(root)->qdisc_hash, i, q, hash) { - if (sch != q && q->ops->cl_ops == &netem_class_ops) { - if (duplicates || - ((struct netem_sched_data *)qdisc_priv(q))->duplicate) - goto err; - } - } - - return 0; - -err: - NL_SET_ERR_MSG(extack, - "netem: cannot mix duplicating netems with other netems in tree"); - return -EINVAL; -} - /* Parse netlink message to set options */ static int netem_change(struct Qdisc *sch, struct nlattr *opt, struct netlink_ext_ack *extack) @@ -1117,11 +1082,6 @@ static int netem_change(struct Qdisc *sch, struct nlattr *opt, q->gap = qopt->gap; q->counter = 0; q->loss = qopt->loss; - - ret = check_netem_in_tree(sch, qopt->duplicate, extack); - if (ret) - goto unlock; - q->duplicate = qopt->duplicate; /* for compatibility with earlier versions. From c35064294eca3dce2432eb5069b123f522a8b877 Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Mon, 25 May 2026 22:45:24 +0800 Subject: [PATCH 454/697] net: hibmcge: disable Relaxed Ordering to fix RX packet corruption [ Upstream commit 463a1271aa26eac992851b9d98cc75bc3cd4a1ed ] When SMMU is disabled, the hibmcge driver may receive corrupted packets. The hardware writes packet data and descriptors to the same page, but with Relaxed Ordering enabled, PCI write transactions may not be strictly ordered. This can cause the driver to observe a valid descriptor before the corresponding packet data is fully written. Fix this by clearing PCI_EXP_DEVCTL_RELAX_EN in the PCI bridge control register to ensure strict write ordering between packet data and descriptors. Fixes: f72e25594061 ("net: hibmcge: Implement rx_poll function to receive packets") Signed-off-by: Jijie Shao Link: https://patch.msgid.link/20260525144525.94884-2-shaojijie@huawei.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c index 0b92a2e5e986..cf0f14aa014c 100644 --- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c +++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c @@ -420,6 +420,9 @@ static int hbg_pci_init(struct pci_dev *pdev) return -ENOMEM; pci_set_master(pdev); + pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN); + pci_save_state(pdev); return 0; } From 0866569fc36a56f568acd3900d354e3505932e09 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 25 May 2026 12:51:15 -0400 Subject: [PATCH 455/697] net/handshake: Use spin_lock_bh for hn_lock [ Upstream commit cc993e0927ec8bd98ea33377ada03295fcda0f24 ] nvmet_tcp_state_change(), a socket callback that runs in BH context, can reach handshake_req_cancel() via nvmet_tcp_schedule_release_queue() and tls_handshake_cancel(). handshake_req_cancel() acquires hn->hn_lock with plain spin_lock(). If a process-context thread on the same CPU holds hn->hn_lock when a softirq invokes the cancel path, the lock attempt deadlocks. This is the only caller that invokes tls_handshake_cancel() from BH context; every other consumer calls it from process context. Deferring the cancel to process context in the NVMe target is not straightforward: nvmet_tcp_schedule_release_queue() must call tls_handshake_cancel() atomically with its state transition to DISCONNECTING. If the cancel were deferred, the handshake completion callback could fire in the window before the cancel runs, observe the unexpected state, and return without dropping its kref on the queue. Reworking that interlock is considerably more invasive than hardening the handshake lock. Convert all hn->hn_lock acquisitions from spin_lock/spin_unlock to spin_lock_bh/spin_unlock_bh so the lock is never taken with softirqs enabled. Fixes: 675b453e0241 ("nvmet-tcp: enable TLS handshake upcall") Signed-off-by: Chuck Lever Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-1-66c616906ead@oracle.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- net/handshake/netlink.c | 4 ++-- net/handshake/request.c | 14 +++++++------- net/handshake/tlshd.c | 2 ++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 7e46d130dce2..394e270cc505 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -203,10 +203,10 @@ static void __net_exit handshake_net_exit(struct net *net) * accepted and are in progress will be destroyed when * the socket is closed. */ - spin_lock(&hn->hn_lock); + spin_lock_bh(&hn->hn_lock); set_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags); list_splice_init(&requests, &hn->hn_requests); - spin_unlock(&hn->hn_lock); + spin_unlock_bh(&hn->hn_lock); while (!list_empty(&requests)) { req = list_first_entry(&requests, struct handshake_req, hr_list); diff --git a/net/handshake/request.c b/net/handshake/request.c index 6b7e3e0bf399..654e55b141cd 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -167,12 +167,12 @@ static bool remove_pending(struct handshake_net *hn, struct handshake_req *req) { bool ret = false; - spin_lock(&hn->hn_lock); + spin_lock_bh(&hn->hn_lock); if (!list_empty(&req->hr_list)) { __remove_pending_locked(hn, req); ret = true; } - spin_unlock(&hn->hn_lock); + spin_unlock_bh(&hn->hn_lock); return ret; } @@ -182,7 +182,7 @@ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class) struct handshake_req *req, *pos; req = NULL; - spin_lock(&hn->hn_lock); + spin_lock_bh(&hn->hn_lock); list_for_each_entry(pos, &hn->hn_requests, hr_list) { if (pos->hr_proto->hp_handler_class != class) continue; @@ -190,7 +190,7 @@ struct handshake_req *handshake_req_next(struct handshake_net *hn, int class) req = pos; break; } - spin_unlock(&hn->hn_lock); + spin_unlock_bh(&hn->hn_lock); return req; } @@ -249,7 +249,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, if (READ_ONCE(hn->hn_pending) >= hn->hn_pending_max) goto out_err; - spin_lock(&hn->hn_lock); + spin_lock_bh(&hn->hn_lock); ret = -EOPNOTSUPP; if (test_bit(HANDSHAKE_F_NET_DRAINING, &hn->hn_flags)) goto out_unlock; @@ -258,7 +258,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, goto out_unlock; if (!__add_pending_locked(hn, req)) goto out_unlock; - spin_unlock(&hn->hn_lock); + spin_unlock_bh(&hn->hn_lock); ret = handshake_genl_notify(net, req->hr_proto, flags); if (ret) { @@ -274,7 +274,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, return 0; out_unlock: - spin_unlock(&hn->hn_lock); + spin_unlock_bh(&hn->hn_lock); out_err: /* Restore original destructor so socket teardown still runs on failure */ req->hr_sk->sk_destruct = req->hr_odestruct; diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c index 8f9532a15f43..af294c6cc717 100644 --- a/net/handshake/tlshd.c +++ b/net/handshake/tlshd.c @@ -425,6 +425,8 @@ EXPORT_SYMBOL(tls_server_hello_psk); * Request cancellation races with request completion. To determine * who won, callers examine the return value from this function. * + * Context: May be called from process or softirq context. + * * Return values: * %true - Uncompleted handshake request was canceled * %false - Handshake request already completed or not found From 25b2fcdea6f6277ecd3d7adc082197e61424d179 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 25 May 2026 12:51:16 -0400 Subject: [PATCH 456/697] nvme-tcp: store negative errno in queue->tls_err [ Upstream commit 9015985b5eb1a90eb86caf5bce1dfcf1aa38f8ad ] nvme_tcp_tls_done() assigns queue->tls_err in three branches. The ENOKEY lookup failure and the EOPNOTSUPP initializer both store negative errnos. The third branch, reached when the handshake layer reports a non-zero status, stores -status. The handshake layer delivers status to the consumer callback as a negative errno; the other in-tree consumers -- xs_tls_handshake_done() and the nvmet target callback -- treat their status argument that way. The extra negation in nvme_tcp_tls_done() flips the sign, leaving tls_err as a positive value (for instance, +EIO), which nvme_tcp_start_tls() then returns to its caller. Drop the extra negation so queue->tls_err uniformly carries a negative errno on failure. Fixes: be8e82caa685 ("nvme-tcp: enable TLS handshake upcall") Signed-off-by: Chuck Lever Reviewed-by: Hannes Reinecke Reviewed-by: Alistair Francis Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-2-66c616906ead@oracle.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- drivers/nvme/host/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index 9a96df1a511c..afdbcff3d482 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -1687,7 +1687,7 @@ static void nvme_tcp_tls_done(void *data, int status, key_serial_t pskid) qid, pskid, status); if (status) { - queue->tls_err = -status; + queue->tls_err = status; goto out_complete; } From 1d4dcfe60fe111823b2339c84d7455e9e89fbfc5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 25 May 2026 12:51:17 -0400 Subject: [PATCH 457/697] net/handshake: Pass negative errno through handshake_complete() [ Upstream commit 6b22d433aa13f68e3cd9534ca9a5f4277bfa01c2 ] handshake_complete() declares status as unsigned int and tls_handshake_done() negates that value (-status) before handing it to the TLS consumer. Consumers match on negative errno constants -- xs_tls_handshake_done() has switch (status) { case 0: case -EACCES: case -ETIMEDOUT: lower_transport->xprt_err = status; break; default: lower_transport->xprt_err = -EACCES; } so the API as designed expects callers to pass positive errno values that the tlshd shim then negates. Three internal callers in handshake_nl_accept_doit(), the net-exit drain, and a kunit test follow kernel convention and pass negative errnos -- -EIO, -ETIMEDOUT, -ETIMEDOUT. The implicit conversion to unsigned int turns -ETIMEDOUT into 0xFFFFFF92; the subsequent -status in tls_handshake_done() wraps back to 110, the consumer's switch falls through, and the xprt reports -EACCES on what should be -ETIMEDOUT or -EIO. Fix the API rather than the call sites. The natural kernel convention is negative errno in, negative errno out. Change handshake_complete() and hp_done to take int status, drop the negation in tls_handshake_done(), and negate once in handshake_nl_done_doit() where status arrives from the wire as an unsigned netlink attribute. The three internal callers were already correct under that convention and need no change. At the same wire boundary, declare MAX_ERRNO as the netlink policy upper bound for HANDSHAKE_A_DONE_STATUS. Attribute validation rejects out-of-range values before handshake_nl_done_doit() runs, and negating a bounded u32 there stays within int range -- closing the UBSAN-visible signed- integer overflow that an unconstrained u32 would invoke. Fixes: 3b3009ea8abb ("net/handshake: Create a NETLINK service for handling handshake requests") Signed-off-by: Chuck Lever Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260525-handshake-file-pin-v3-3-66c616906ead@oracle.com Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin --- Documentation/netlink/specs/handshake.yaml | 8 ++++++++ net/handshake/genl.c | 3 ++- net/handshake/genl.h | 1 + net/handshake/handshake-test.c | 2 +- net/handshake/handshake.h | 4 ++-- net/handshake/netlink.c | 2 +- net/handshake/request.c | 2 +- net/handshake/tlshd.c | 4 ++-- 8 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Documentation/netlink/specs/handshake.yaml b/Documentation/netlink/specs/handshake.yaml index 95c3fade7a8d..1024297b3851 100644 --- a/Documentation/netlink/specs/handshake.yaml +++ b/Documentation/netlink/specs/handshake.yaml @@ -12,6 +12,12 @@ protocol: genetlink doc: Netlink protocol to request a transport layer security handshake. definitions: + - + type: const + name: max-errno + value: 4095 + header: linux/err.h + scope: kernel - type: enum name: handler-class @@ -80,6 +86,8 @@ attribute-sets: - name: status type: u32 + checks: + max: max-errno - name: sockfd type: s32 diff --git a/net/handshake/genl.c b/net/handshake/genl.c index f55d14d7b726..a5fa8b27f224 100644 --- a/net/handshake/genl.c +++ b/net/handshake/genl.c @@ -9,6 +9,7 @@ #include "genl.h" #include +#include /* HANDSHAKE_CMD_ACCEPT - do */ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HANDLER_CLASS + 1] = { @@ -17,7 +18,7 @@ static const struct nla_policy handshake_accept_nl_policy[HANDSHAKE_A_ACCEPT_HAN /* HANDSHAKE_CMD_DONE - do */ static const struct nla_policy handshake_done_nl_policy[HANDSHAKE_A_DONE_REMOTE_AUTH + 1] = { - [HANDSHAKE_A_DONE_STATUS] = { .type = NLA_U32, }, + [HANDSHAKE_A_DONE_STATUS] = NLA_POLICY_MAX(NLA_U32, MAX_ERRNO), [HANDSHAKE_A_DONE_SOCKFD] = { .type = NLA_S32, }, [HANDSHAKE_A_DONE_REMOTE_AUTH] = { .type = NLA_U32, }, }; diff --git a/net/handshake/genl.h b/net/handshake/genl.h index ae72a596f6cc..684e5fd68448 100644 --- a/net/handshake/genl.h +++ b/net/handshake/genl.h @@ -10,6 +10,7 @@ #include #include +#include int handshake_nl_accept_doit(struct sk_buff *skb, struct genl_info *info); int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info); diff --git a/net/handshake/handshake-test.c b/net/handshake/handshake-test.c index 55442b2f518a..df3948e807a0 100644 --- a/net/handshake/handshake-test.c +++ b/net/handshake/handshake-test.c @@ -25,7 +25,7 @@ static int test_accept_func(struct handshake_req *req, struct genl_info *info, return 0; } -static void test_done_func(struct handshake_req *req, unsigned int status, +static void test_done_func(struct handshake_req *req, int status, struct genl_info *info) { } diff --git a/net/handshake/handshake.h b/net/handshake/handshake.h index a48163765a7a..2289b0e274f4 100644 --- a/net/handshake/handshake.h +++ b/net/handshake/handshake.h @@ -57,7 +57,7 @@ struct handshake_proto { int (*hp_accept)(struct handshake_req *req, struct genl_info *info, int fd); void (*hp_done)(struct handshake_req *req, - unsigned int status, + int status, struct genl_info *info); void (*hp_destroy)(struct handshake_req *req); }; @@ -86,7 +86,7 @@ struct handshake_req *handshake_req_hash_lookup(struct sock *sk); struct handshake_req *handshake_req_next(struct handshake_net *hn, int class); int handshake_req_submit(struct socket *sock, struct handshake_req *req, gfp_t flags); -void handshake_complete(struct handshake_req *req, unsigned int status, +void handshake_complete(struct handshake_req *req, int status, struct genl_info *info); bool handshake_req_cancel(struct sock *sk); diff --git a/net/handshake/netlink.c b/net/handshake/netlink.c index 394e270cc505..d8211e0ba75c 100644 --- a/net/handshake/netlink.c +++ b/net/handshake/netlink.c @@ -161,7 +161,7 @@ int handshake_nl_done_doit(struct sk_buff *skb, struct genl_info *info) status = -EIO; if (info->attrs[HANDSHAKE_A_DONE_STATUS]) - status = nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]); + status = -(int)nla_get_u32(info->attrs[HANDSHAKE_A_DONE_STATUS]); handshake_complete(req, status, info); sockfd_put(sock); diff --git a/net/handshake/request.c b/net/handshake/request.c index 654e55b141cd..62efb7e32730 100644 --- a/net/handshake/request.c +++ b/net/handshake/request.c @@ -284,7 +284,7 @@ int handshake_req_submit(struct socket *sock, struct handshake_req *req, } EXPORT_SYMBOL(handshake_req_submit); -void handshake_complete(struct handshake_req *req, unsigned int status, +void handshake_complete(struct handshake_req *req, int status, struct genl_info *info) { struct sock *sk = req->hr_sk; diff --git a/net/handshake/tlshd.c b/net/handshake/tlshd.c index af294c6cc717..7567150c2a4f 100644 --- a/net/handshake/tlshd.c +++ b/net/handshake/tlshd.c @@ -93,7 +93,7 @@ static void tls_handshake_remote_peerids(struct tls_handshake_req *treq, * */ static void tls_handshake_done(struct handshake_req *req, - unsigned int status, struct genl_info *info) + int status, struct genl_info *info) { struct tls_handshake_req *treq = handshake_req_private(req); @@ -104,7 +104,7 @@ static void tls_handshake_done(struct handshake_req *req, if (!status) set_bit(HANDSHAKE_F_REQ_SESSION, &req->hr_flags); - treq->th_consumer_done(treq->th_consumer_data, -status, + treq->th_consumer_done(treq->th_consumer_data, status, treq->th_peerid[0]); } From f39049304ba655ffcbb92edbdf8c51a1f1210bed Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Tue, 26 May 2026 18:51:52 +0800 Subject: [PATCH 458/697] Bluetooth: l2cap: clear chan->ident on ECRED reconfiguration success [ Upstream commit 00e1950716c6ed67d74777b2db286b0fa23b4be9 ] l2cap_ecred_reconf_rsp() returns early on success without clearing chan->ident. Every other L2CAP response handler (l2cap_ecred_conn_rsp, l2cap_le_connect_rsp, l2cap_config_rsp) clears chan->ident after a successful transaction to prevent the channel from matching subsequent responses with the recycled ident value. A remote attacker that completed a reconfiguration as the peer can replay a failure response with the stale ident, causing the kernel to match and destroy the already-established channel via l2cap_chan_del(chan, ECONNRESET). Clear chan->ident for all matching channels on success, and harden the failure path by using l2cap_chan_hold_unless_zero() consistent with other L2CAP handlers (l2cap_le_command_rej, __l2cap_get_chan_by_ident). Fixes: 15f02b910562 ("Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode") Signed-off-by: Zhenghang Xiao Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 87ebe81277c5..57f5e3c7429e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5466,14 +5466,20 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn, BT_DBG("result 0x%4.4x", result); - if (!result) + if (!result) { + list_for_each_entry(chan, &conn->chan_l, list) { + if (chan->ident == cmd->ident) + chan->ident = 0; + } return 0; + } list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { if (chan->ident != cmd->ident) continue; - l2cap_chan_hold(chan); + if (!l2cap_chan_hold_unless_zero(chan)) + continue; l2cap_chan_lock(chan); l2cap_chan_del(chan, ECONNRESET); From 41e29548b5e8b5e5fcf708786b3bea67cab107fa Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 11 May 2026 12:09:42 -0400 Subject: [PATCH 459/697] Bluetooth: L2CAP: Fix possible crash on l2cap_ecred_conn_rsp [ Upstream commit 41c2713b204e6cb6a94587bc6bf6935107df5479 ] If dcid is received for an already-assigned destination CID the spec requires that both channels to be discarded, but calling l2cap_chan_del may invalidate the tmp cursor created by list_for_each_entry_safe and in fact it is the wrong procedure as the chan->dcid may be assigned previously it really needs to be disconnected. Calling l2cap_chan_clone directly may still lead to l2cap_chan_del so instead schedule l2cap_chan_timeout with delay 0 to close the channel asynchronously. Fixes: 15f02b910562 ("Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode") Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/l2cap_core.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 57f5e3c7429e..9805908e6c6a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5268,6 +5268,7 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, cmd_len -= sizeof(*rsp); list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { + struct l2cap_chan *orig; u16 dcid; if (chan->ident != cmd->ident || @@ -5289,8 +5290,10 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, BT_DBG("dcid[%d] 0x%4.4x", i, dcid); + orig = __l2cap_get_chan_by_dcid(conn, dcid); + /* Check if dcid is already in use */ - if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) { + if (dcid && orig) { /* If a device receives a * L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an * already-assigned Destination CID, then both the @@ -5299,10 +5302,24 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, */ l2cap_chan_del(chan, ECONNREFUSED); l2cap_chan_unlock(chan); - chan = __l2cap_get_chan_by_dcid(conn, dcid); - l2cap_chan_lock(chan); - l2cap_chan_del(chan, ECONNRESET); - l2cap_chan_unlock(chan); + + /* Check that the dcid channel mode is + * L2CAP_MODE_EXT_FLOWCTL since this procedure is only + * valid for that mode and shouldn't disconnect a dcid + * in other modes. + */ + if (orig->mode == L2CAP_MODE_EXT_FLOWCTL) { + l2cap_chan_lock(orig); + /* Disconnect the original channel as it may be + * considered connected since dcid has already + * been assigned; don't call l2cap_chan_close + * directly since that could lead to + * l2cap_chan_del and then removing the channel + * from the list while we're iterating over it. + */ + __set_chan_timer(orig, 0); + l2cap_chan_unlock(orig); + } continue; } From 47330cc875b36a1cf7b3543cb2cf90a7c603ce0e Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 26 May 2026 10:50:58 -0300 Subject: [PATCH 460/697] Bluetooth: hci_sync: Set HCI_CMD_DRAIN_WORKQUEUE during device close [ Upstream commit 525daaea459fc215f432de1b8debbd9144bf97b0 ] Since hci_dev_close_sync() can now be called during the reset path, we should also set HCI_CMD_DRAIN_WORKQUEUE. This avoids queuing timeouts while the hdev workqueue is being drained. Fixes: 877afadad2dc ("Bluetooth: When HCI work queue is drained, only queue chained work") Signed-off-by: Heitor Alves de Siqueira Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_sync.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index f498ab28f1aa..73e429c41e17 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -5246,6 +5246,12 @@ int hci_dev_close_sync(struct hci_dev *hdev) bt_dev_dbg(hdev, ""); + /* Set HCI_DRAIN_WORKQUEUE flag to prevent queuing work during + * reset/close. See hci_cmd_work() and handle_cmd_cnt_and_timer(). + */ + hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE); + synchronize_rcu(); + if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) { disable_delayed_work(&hdev->power_off); disable_delayed_work(&hdev->ncmd_timer); @@ -5269,6 +5275,7 @@ int hci_dev_close_sync(struct hci_dev *hdev) if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { cancel_delayed_work_sync(&hdev->cmd_timer); + hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE); return err; } @@ -5368,6 +5375,7 @@ int hci_dev_close_sync(struct hci_dev *hdev) /* Clear flags */ hdev->flags &= BIT(HCI_RAW); hci_dev_clear_volatile_flags(hdev); + hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE); memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); From ae2eac5e9cfeec8631d7a08594a25176c68ce0f3 Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 26 May 2026 10:50:59 -0300 Subject: [PATCH 461/697] Bluetooth: hci_sync: Reset device counters in hci_dev_close_sync() [ Upstream commit cdf88b35e06f1b385f7f6228060ae541d44fbb72 ] Before resetting or closing the device, protocol counters should also be zeroed. Fixes: d0b137062b2d ("Bluetooth: hci_sync: Rework init stages") Signed-off-by: Heitor Alves de Siqueira Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin --- net/bluetooth/hci_sync.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 73e429c41e17..277de808ebeb 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -5338,6 +5338,10 @@ int hci_dev_close_sync(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); + hdev->acl_cnt = 0; + hdev->sco_cnt = 0; + hdev->le_cnt = 0; + hdev->iso_cnt = 0; if (hci_test_quirk(hdev, HCI_QUIRK_RESET_ON_CLOSE) && !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { set_bit(HCI_INIT, &hdev->flags); From b6cdbb681ce13b449b3c35d0082daacf099270cc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 May 2026 09:35:27 +0200 Subject: [PATCH 462/697] gpio: adnp: fix flow control regression caused by scoped_guard() [ Upstream commit a5c627d90809b793fc053849b3a00609db305776 ] scoped_guard() is implemented as a for loop. Using it to protect code using the continue statement changes the flow as we now only break out of the hidden loop inside scoped_guard(), not the original for loop. Use a regular code block instead. Fixes: c7fe19ed3973 ("gpio: adnp: use lock guards for the I2C lock") Reported-by: David Lechner Closes: https://lore.kernel.org/all/cde2abb2-4cc8-4fc9-b34a-0c5d2b95779f@baylibre.com/ Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260522073527.9812-1-bartosz.golaszewski@oss.qualcomm.com Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-adnp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index e5ac2d211013..fe5bcaa90496 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -237,7 +237,9 @@ static irqreturn_t adnp_irq(int irq, void *data) unsigned long pending; int err; - scoped_guard(mutex, &adnp->i2c_lock) { + { + guard(mutex)(&adnp->i2c_lock); + err = adnp_read(adnp, GPIO_PLR(adnp) + i, &level); if (err < 0) continue; From 5d43c71fa8e1b5f940730c11a141839d89e696ec Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 25 May 2026 10:15:16 +0300 Subject: [PATCH 463/697] gpio: virtuser: Fix uninitialized data bug in gpio_virtuser_direction_do_write() [ Upstream commit 8a122b5e72cc0043705f0d524bcd15f0c0b3ec15 ] If *ppos is non-zero (user-space write split over multiple calls to write()) then simple_write_to_buffer() won't initialize the start of the buffer. Really, non-zero values for *ppos aren't going to work at all. Check for that and return -EINVAL at the start of the function. Fixes: 91581c4b3f29 ("gpio: virtuser: new virtual testing driver for the GPIO API") Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/ahP3BJWWy-m_qI0X@stanley.mountain Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-virtuser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 252fec5ea383..1901b4ba558f 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -399,7 +399,7 @@ static ssize_t gpio_virtuser_direction_do_write(struct file *file, char buf[32], *trimmed; int ret, dir, val = 0; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); @@ -626,7 +626,7 @@ static ssize_t gpio_virtuser_consumer_write(struct file *file, char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2]; int ret; - if (count >= sizeof(buf)) + if (*ppos != 0 || count >= sizeof(buf)) return -EINVAL; ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos, From e2fabb984bfdc66b9e514fbcd6dcfc6d7d5187d6 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Tue, 26 May 2026 19:02:45 +0200 Subject: [PATCH 464/697] gpio: rockchip: convert bank->clk to devm_clk_get_enabled() [ Upstream commit 3e46c18d5d87f063a93ae0fe7662fbf6660459d5 ] The bank->clk was previously obtained via of_clk_get() and manually prepared/enabled. However, it was missing a corresponding clk_put() in both the error paths and the remove function, leading to a reference leak. Convert the allocation to devm_clk_get_enabled(), which also properly propagates failures from clk_prepare_enable() that were previously ignored. The GPIO bank device uses the same OF node as the previous of_clk_get() call, so devm_clk_get_enabled(dev, NULL) correctly resolves the same clock provider entry. Fix the reference leak and simplify the code by removing the manual clk_disable_unprepare() calls in the probe error paths and in the remove function. Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260526171050.12785-2-scardracs@disroot.org Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-rockchip.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 0fff4a699f12..f910220141f7 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -656,11 +656,10 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) if (!bank->irq) return -EINVAL; - bank->clk = of_clk_get(bank->of_node, 0); + bank->clk = devm_clk_get_enabled(bank->dev, NULL); if (IS_ERR(bank->clk)) return PTR_ERR(bank->clk); - clk_prepare_enable(bank->clk); id = readl(bank->reg_base + gpio_regs_v2.version_id); switch (id) { @@ -672,7 +671,6 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) bank->db_clk = of_clk_get(bank->of_node, 1); if (IS_ERR(bank->db_clk)) { dev_err(bank->dev, "cannot find debounce clk\n"); - clk_disable_unprepare(bank->clk); return -EINVAL; } break; @@ -751,7 +749,6 @@ static int rockchip_gpio_probe(struct platform_device *pdev) ret = rockchip_gpiolib_register(bank); if (ret) { - clk_disable_unprepare(bank->clk); mutex_unlock(&bank->deferred_lock); return ret; } @@ -792,7 +789,6 @@ static void rockchip_gpio_remove(struct platform_device *pdev) { struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); - clk_disable_unprepare(bank->clk); gpiochip_remove(&bank->gpio_chip); } From 7f945f7f10f442270518dfd768d230227c495fcf Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Tue, 26 May 2026 19:02:46 +0200 Subject: [PATCH 465/697] gpio: rockchip: teardown bugs and resource leaks [ Upstream commit 9500077678230e36d22bf16d2b9539c13e59a801 ] Address several teardown issues and resource leaks in the driver's remove path and error handling: 1. Debounce clock reference leak: The debounce clock (bank->db_clk) is obtained using of_clk_get() which increments the clock's reference count, but clk_put() is never called. Register a devm action to cleanly release it on unbind. Note that of_clk_get(..., 1) remains necessary over devm_clk_get() because the DT binding does not define clock-names, precluding name-based lookup. 2. Unregistered chained IRQ handler: The chained IRQ handler is not disconnected in remove(). If a stray interrupt fires after the driver is removed, the kernel attempts to execute a stale handler, leading to a panic. Fix this by clearing the handler in remove(). 3. IRQ domain leak: The linear IRQ domain and its generic chips are allocated manually during probe but never removed. Remove the IRQ domain during driver teardown to free the associated generic chips and mappings. Fixes: 936ee2675eee ("gpio/rockchip: add driver for rockchip gpio") Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Link: https://patch.msgid.link/20260526171050.12785-3-scardracs@disroot.org [Bartosz: don't emit an error message on devres allocation failure] Signed-off-by: Bartosz Golaszewski Signed-off-by: Sasha Levin --- drivers/gpio/gpio-rockchip.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index f910220141f7..1ef0ba956cfd 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -638,10 +638,17 @@ static int rockchip_gpiolib_register(struct rockchip_pin_bank *bank) return ret; } +static void rockchip_clk_put(void *data) +{ + struct clk *clk = data; + + clk_put(clk); +} + static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) { struct resource res; - int id = 0; + int id = 0, ret; if (of_address_to_resource(bank->of_node, 0, &res)) { dev_err(bank->dev, "cannot find IO resource for bank\n"); @@ -673,6 +680,11 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) dev_err(bank->dev, "cannot find debounce clk\n"); return -EINVAL; } + + ret = devm_add_action_or_reset(bank->dev, rockchip_clk_put, + bank->db_clk); + if (ret) + return ret; break; case GPIO_TYPE_V1: bank->gpio_regs = &gpio_regs_v1; @@ -789,6 +801,9 @@ static void rockchip_gpio_remove(struct platform_device *pdev) { struct rockchip_pin_bank *bank = platform_get_drvdata(pdev); + irq_set_chained_handler_and_data(bank->irq, NULL, NULL); + if (bank->domain) + irq_domain_remove(bank->domain); gpiochip_remove(&bank->gpio_chip); } From da87896f34e0a51489811d1a684e2953099ca98f Mon Sep 17 00:00:00 2001 From: Dipayaan Roy Date: Mon, 25 May 2026 01:08:24 -0700 Subject: [PATCH 466/697] net: mana: Add NULL guards in teardown path to prevent panic on attach failure [ Upstream commit 17bfe0a8c014ee1d542ad352cd6a0a505361664a ] When queue allocation fails partway through, the error cleanup frees and NULLs apc->tx_qp and apc->rxqs. Multiple teardown paths such as mana_remove(), mana_change_mtu() recovery, and internal error handling in mana_alloc_queues() can subsequently call into functions that dereference these pointers without NULL checks: - mana_chn_setxdp() dereferences apc->rxqs[0], causing a NULL pointer dereference panic (CR2: 0000000000000000 at mana_chn_setxdp+0x26). - mana_destroy_vport() iterates apc->rxqs without a NULL check. - mana_fence_rqs() iterates apc->rxqs without a NULL check. - mana_dealloc_queues() iterates apc->tx_qp without a NULL check. Add NULL guards for apc->rxqs in mana_fence_rqs(), mana_destroy_vport(), and before the mana_chn_setxdp() call. Add a NULL guard for apc->tx_qp in mana_dealloc_queues() to skip TX queue draining when TX queues were never allocated or already freed. Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network Adapter (MANA)") Reviewed-by: Haiyang Zhang Signed-off-by: Dipayaan Roy Link: https://patch.msgid.link/20260525081129.1230035-2-dipayanroy@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/mana_en.c | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index d1eb77d54042..6ef2a3ee44c6 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -1720,6 +1720,9 @@ static void mana_fence_rqs(struct mana_port_context *apc) struct mana_rxq *rxq; int err; + if (!apc->rxqs) + return; + for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { rxq = apc->rxqs[rxq_idx]; err = mana_fence_rq(apc, rxq); @@ -2830,13 +2833,16 @@ static void mana_destroy_vport(struct mana_port_context *apc) struct mana_rxq *rxq; u32 rxq_idx; - for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { - rxq = apc->rxqs[rxq_idx]; - if (!rxq) - continue; + if (apc->rxqs) { - mana_destroy_rxq(apc, rxq, true); - apc->rxqs[rxq_idx] = NULL; + for (rxq_idx = 0; rxq_idx < apc->num_queues; rxq_idx++) { + rxq = apc->rxqs[rxq_idx]; + if (!rxq) + continue; + + mana_destroy_rxq(apc, rxq, true); + apc->rxqs[rxq_idx] = NULL; + } } mana_destroy_txq(apc); @@ -3241,7 +3247,8 @@ static int mana_dealloc_queues(struct net_device *ndev) if (apc->port_is_up) return -EINVAL; - mana_chn_setxdp(apc, NULL); + if (apc->rxqs) + mana_chn_setxdp(apc, NULL); if (gd->gdma_context->is_pf && !apc->ac->bm_hostmode) mana_pf_deregister_filter(apc); @@ -3259,33 +3266,38 @@ static int mana_dealloc_queues(struct net_device *ndev) * number of queues. */ - for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; - tsleep = 1000; - while (atomic_read(&txq->pending_sends) > 0 && - time_before(jiffies, timeout)) { - usleep_range(tsleep, tsleep + 1000); - tsleep <<= 1; - } - if (atomic_read(&txq->pending_sends)) { - err = pcie_flr(to_pci_dev(gd->gdma_context->dev)); - if (err) { - netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n", - err, atomic_read(&txq->pending_sends), - txq->gdma_txq_id); + if (apc->tx_qp) { + for (i = 0; i < apc->num_queues; i++) { + txq = &apc->tx_qp[i].txq; + tsleep = 1000; + while (atomic_read(&txq->pending_sends) > 0 && + time_before(jiffies, timeout)) { + usleep_range(tsleep, tsleep + 1000); + tsleep <<= 1; + } + if (atomic_read(&txq->pending_sends)) { + err = + pcie_flr(to_pci_dev(gd->gdma_context->dev)); + if (err) { + netdev_err(ndev, "flr failed %d with %d pkts pending in txq %u\n", + err, + atomic_read(&txq->pending_sends), + txq->gdma_txq_id); + } + break; } - break; } - } - for (i = 0; i < apc->num_queues; i++) { - txq = &apc->tx_qp[i].txq; - while ((skb = skb_dequeue(&txq->pending_skbs))) { - mana_unmap_skb(skb, apc); - dev_kfree_skb_any(skb); + for (i = 0; i < apc->num_queues; i++) { + txq = &apc->tx_qp[i].txq; + while ((skb = skb_dequeue(&txq->pending_skbs))) { + mana_unmap_skb(skb, apc); + dev_kfree_skb_any(skb); + } + atomic_set(&txq->pending_sends, 0); } - atomic_set(&txq->pending_sends, 0); } + /* We're 100% sure the queues can no longer be woken up, because * we're sure now mana_poll_tx_cq() can't be running. */ From c4152b4e28b3e550ec99351bf900e2c24c2608cc Mon Sep 17 00:00:00 2001 From: Dipayaan Roy Date: Mon, 25 May 2026 01:08:25 -0700 Subject: [PATCH 467/697] net: mana: Skip redundant detach on already-detached port [ Upstream commit 5b05aa36ee24297d7296ca58dfd8c448d0e4cda3 ] When mana_per_port_queue_reset_work_handler() runs after a previous detach succeeded but attach failed, the port is left in a detached state with apc->tx_qp and apc->rxqs already freed. Calling mana_detach() again unconditionally leads to NULL pointer dereferences during queue teardown. Add an early exit in mana_detach() when the port is already in detached state (!netif_device_present) for non-close callers, making it safe to call idempotently. This allows the queue reset handler and other recovery paths to simply retry mana_attach() without redundant teardown. Fixes: 3b194343c250 ("net: mana: Implement ndo_tx_timeout and serialize queue resets per port.") Reviewed-by: Haiyang Zhang Signed-off-by: Dipayaan Roy Link: https://patch.msgid.link/20260525081129.1230035-3-dipayanroy@linux.microsoft.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/ethernet/microsoft/mana/mana_en.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 6ef2a3ee44c6..1f723c0ea128 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -3322,6 +3322,12 @@ int mana_detach(struct net_device *ndev, bool from_close) ASSERT_RTNL(); + /* If already detached (indicates detach succeeded but attach failed + * previously). Now skip mana detach and just retry mana_attach. + */ + if (!from_close && !netif_device_present(ndev)) + return 0; + apc->port_st_save = apc->port_is_up; apc->port_is_up = false; From 68667ee4c7dadf7f63167234e2a1af09b3f7874e Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Wed, 27 May 2026 11:24:11 +0800 Subject: [PATCH 468/697] sctp: fix race between sctp_wait_for_connect and peeloff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit f14fe6395a8b3d961a61e138ad7b36ba3626dd4e ] sctp_wait_for_connect() drops and re-acquires the socket lock while waiting for the association to reach ESTABLISHED state. During this window, another thread can peeloff the association to a new socket via getsockopt(SCTP_SOCKOPT_PEELOFF), changing asoc->base.sk. After re-acquiring the old socket lock, sctp_wait_for_connect() returns success without noticing the migration — the caller then accesses the association under the wrong lock in sctp_datamsg_from_user(). Add the same sk != asoc->base.sk check that sctp_wait_for_sndbuf() already has, returning an error if the association was migrated while we slept. Fixes: 668c9beb9020 ("sctp: implement assign_number for sctp_stream_interleave") Signed-off-by: Zhenghang Xiao Acked-by: Xin Long Link: https://patch.msgid.link/20260527032411.60959-1-kipreyyy@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/sctp/socket.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2c5ad5398490..c763eb3296b3 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -9350,6 +9350,8 @@ static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p) release_sock(sk); current_timeo = schedule_timeout(current_timeo); lock_sock(sk); + if (sk != asoc->base.sk) + goto do_error; *timeo_p = current_timeo; } From b62e2b2b4a50953ca952f3cd3f77dd62dc50fd5d Mon Sep 17 00:00:00 2001 From: Jingguo Tan Date: Wed, 27 May 2026 10:33:01 +0800 Subject: [PATCH 469/697] vsock/virtio: bind uarg before filling zerocopy skb [ Upstream commit 1e584c304cfb94a759417130b1fc6d30b30c4cce ] virtio_transport_send_pkt_info() allocates or reuses the zerocopy uarg before entering the send loop, but virtio_transport_alloc_skb() still fills the skb before it inherits that uarg. When fixed-buffer vectored zerocopy hits MAX_SKB_FRAGS, io_sg_from_iter() may partially attach managed frags and return -EMSGSIZE. The rollback path call kfree_skb() to free an skb that carries SKBFL_MANAGED_FRAG_REFS but no uarg, so skb_release_data() falls through to ordinary frag unref. Pass the uarg into virtio_transport_alloc_skb() and bind it immediately before virtio_transport_fill_skb(). This keeps control or no-payload skbs untouched while ensuring success and rollback share one lifetime rule. Fixes: 581512a6dc93 ("vsock/virtio: MSG_ZEROCOPY flag support") Signed-off-by: Lin Ma Signed-off-by: Rongzhen Cui Signed-off-by: Jingguo Tan Acked-by: Arseniy Krasnov Acked-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Link: https://patch.msgid.link/20260527023301.1075581-1-malin89@huawei.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/vmw_vsock/virtio_transport_common.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index 1e07d3b1a0e8..c925b5c5b35a 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -207,6 +207,7 @@ static u16 virtio_transport_get_type(struct sock *sk) static struct sk_buff *virtio_transport_alloc_skb(struct virtio_vsock_pkt_info *info, size_t payload_len, bool zcopy, + struct ubuf_info *uarg, u32 src_cid, u32 src_port, u32 dst_cid, @@ -247,6 +248,12 @@ static struct sk_buff *virtio_transport_alloc_skb(struct virtio_vsock_pkt_info * if (info->msg && payload_len > 0) { int err; + /* Bind the zerocopy lifetime before filling frags so error + * rollback frees managed fixed-buffer pages through + * the uarg-aware path. + */ + skb_zcopy_set(skb, uarg, NULL); + err = virtio_transport_fill_skb(skb, info, payload_len, zcopy); if (err) goto out; @@ -366,6 +373,7 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, skb_len = min(max_skb_len, rest_len); skb = virtio_transport_alloc_skb(info, skb_len, can_zcopy, + uarg, src_cid, src_port, dst_cid, dst_port); if (!skb) { @@ -373,8 +381,6 @@ static int virtio_transport_send_pkt_info(struct vsock_sock *vsk, break; } - skb_zcopy_set(skb, uarg, NULL); - virtio_transport_inc_tx_pkt(vvs, skb); ret = t_ops->send_pkt(skb); @@ -1161,7 +1167,7 @@ static int virtio_transport_reset_no_sock(const struct virtio_transport *t, if (!t) return -ENOTCONN; - reply = virtio_transport_alloc_skb(&info, 0, false, + reply = virtio_transport_alloc_skb(&info, 0, false, NULL, le64_to_cpu(hdr->dst_cid), le32_to_cpu(hdr->dst_port), le64_to_cpu(hdr->src_cid), From dc36a04621dcc2447dae428709207810b6c06e14 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Wed, 27 May 2026 13:31:30 +0800 Subject: [PATCH 470/697] ipv6: fix possible infinite loop in rt6_fill_node() [ Upstream commit 9f72412bcf60144f252b0d6205106abf14344abc ] Sashiko reported this issue [1]. Apply the same fix as commit f8d8ce1b515a ("ipv6: fix possible infinite loop in fib6_info_uses_dev()"). Writers holding tb6_lock can list_del_rcu(&rt->fib6_siblings) without waiting for RCU readers; rt->fib6_siblings.next then still points into the old ring and this softirq-side walker never reaches &rt->fib6_siblings, causing a CPU stall. fib6_del_route() always WRITE_ONCE()s rt->fib6_nsiblings to 0 before list_del_rcu(), so an inside-loop check is a reliable detach signal. [1] https://sashiko.dev/#/patchset/20260526020227.4857-1-jiayuan.chen%40linux.dev Fixes: d9ccb18f83ea ("ipv6: Fix soft lockups in fib6_select_path under high next hop churn") Signed-off-by: Jiayuan Chen Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260527053133.180695-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 446f4de7d6a2..cf9546047b57 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5892,6 +5892,8 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb, goto nla_put_failure; } + if (!READ_ONCE(rt->fib6_nsiblings)) + break; } rcu_read_unlock(); From ab9a10969a907b472a0196d999c08ff7144172e3 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Wed, 27 May 2026 13:31:31 +0800 Subject: [PATCH 471/697] ipv6: fix possible infinite loop in fib6_select_path() [ Upstream commit 9c7da87c2dc860bb17ca1ece942495d28b1ce3b9 ] Found while auditing the same pattern Sashiko reported in rt6_fill_node() [1]. Apply the same fix as commit f8d8ce1b515a ("ipv6: fix possible infinite loop in fib6_info_uses_dev()"). Writers holding tb6_lock can list_del_rcu(&first->fib6_siblings) without waiting for RCU readers; first->fib6_siblings.next then still points into the old ring and this softirq-side walker never reaches &first->fib6_siblings as its terminator. fib6_purge_rt() always WRITE_ONCE()s first->fib6_nsiblings to 0 before list_del_rcu(), so an inside-loop check is a reliable detach signal. [1] https://sashiko.dev/#/patchset/20260526020227.4857-1-jiayuan.chen%40linux.dev Fixes: d9ccb18f83ea ("ipv6: Fix soft lockups in fib6_select_path under high next hop churn") Signed-off-by: Jiayuan Chen Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260527053133.180695-2-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/ipv6/route.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index cf9546047b57..f89220929c4e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -481,6 +481,9 @@ void fib6_select_path(const struct net *net, struct fib6_result *res, const struct fib6_nh *nh = sibling->fib6_nh; int nh_upper_bound; + if (!READ_ONCE(first->fib6_nsiblings)) + break; + nh_upper_bound = atomic_read(&nh->fib_nh_upper_bound); if (hash > nh_upper_bound) continue; From 814be4a0924b6b3dab092670b566d6368c076cb9 Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Thu, 28 May 2026 19:43:53 +0100 Subject: [PATCH 472/697] net: skbuff: fix pskb_carve leaking zcopy pages [ Upstream commit ff6e798c2eac3ebd0501ad7e796f583fab928de8 ] When SKBFL_MANAGED_FRAG_REFS is set, frag pages are not refcounted but their lifetime is controlled by the attached ubuf_info. To make a copy of the skb_shared_info, we either should clear the flag and reference the frags, or keep the flag and have frags unreferenced. pskb_carve_inside_header() and pskb_carve_inside_nonlinear() don't follow the rule and thus can leak page references. Let's clear SKBFL_MANAGED_FRAG_REFS from the original skb to fix it. It's the simplest way to address it, but there are more performant ways to do that if it ever becomes a problem. Link: https://lore.kernel.org/all/20260523085809.26331-1-nvminh232@clc.fitus.edu.vn/ Fixes: 753f1ca4e1e50 ("net: introduce managed frags infrastructure") Reported-by: Minh Nguyen Reported-by: Willem de Bruijn Signed-off-by: Pavel Begunkov Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/1e2086aa69217d7f9c8da3d38f5be7160f1b4cd1.1779993185.git.asml.silence@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- net/core/skbuff.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6618bfa70ca4..a52aa79b0fdb 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -6772,6 +6772,11 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, skb_copy_from_linear_data_offset(skb, off, data, new_hlen); skb->len -= off; + /* Remove SKBFL_MANAGED_FRAG_REFS instead of trying to honour it + * while refcounting frags below. + */ + skb_zcopy_downgrade_managed(skb); + memcpy((struct skb_shared_info *)(data + size), skb_shinfo(skb), offsetof(struct skb_shared_info, @@ -6883,6 +6888,11 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, return -ENOMEM; size = SKB_WITH_OVERHEAD(size); + /* Remove SKBFL_MANAGED_FRAG_REFS instead of trying to honour it + * while refcounting frags below. + */ + skb_zcopy_downgrade_managed(skb); + memcpy((struct skb_shared_info *)(data + size), skb_shinfo(skb), offsetof(struct skb_shared_info, frags[0])); if (skb_orphan_frags(skb, gfp_mask)) { From e250b672d40a9e0d97a8895cbb301248bea0fce6 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 20 Dec 2025 10:33:26 +0000 Subject: [PATCH 473/697] media: rc: fix race between unregister and urb/irq callbacks [ Upstream commit dccc0c3ddf8f16071736f98a7d6dd46a2d43e037 ] Some rc device drivers have a race condition between rc_unregister_device() and irq or urb callbacks. This is because rc_unregister_device() does two things, it marks the device as unregistered so no new commands can be issued and then it calls rc_free_device(). This means the driver has no chance to cancel any pending urb callbacks or interrupts after the device has been marked as unregistered. Those callbacks may access struct rc_dev or its members (e.g. struct ir_raw_event_ctrl), which have been freed by rc_free_device(). This change removes the implicit call to rc_free_device() from rc_unregister_device(). This means that device drivers can call rc_unregister_device() in their remove or disconnect function, then cancel all the urbs and interrupts before explicitly calling rc_free_device(). Note this is an alternative fix for an issue found by Haotian Zhang, see the Closes: tags. Reported-by: Haotian Zhang Closes: https://lore.kernel.org/linux-media/20251114101432.2566-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114101418.2548-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114101346.2530-1-vulab@iscas.ac.cn/ Closes: https://lore.kernel.org/linux-media/20251114090605.2413-1-vulab@iscas.ac.cn/ Reviewed-by: Patrice Chotard Signed-off-by: Sean Young Signed-off-by: Hans Verkuil Stable-dep-of: 646ebdd31058 ("media: rc: ttusbir: fix inverted error logic") Signed-off-by: Sasha Levin --- drivers/gpu/drm/bridge/sil-sii8620.c | 1 + drivers/hid/hid-picolcd_cir.c | 1 + drivers/media/cec/core/cec-core.c | 2 +- drivers/media/common/siano/smsir.c | 1 + drivers/media/i2c/ir-kbd-i2c.c | 2 ++ drivers/media/pci/bt8xx/bttv-input.c | 3 ++- drivers/media/pci/cx23885/cx23885-input.c | 1 + drivers/media/pci/cx88/cx88-input.c | 3 ++- drivers/media/pci/dm1105/dm1105.c | 1 + drivers/media/pci/mantis/mantis_input.c | 1 + drivers/media/pci/saa7134/saa7134-input.c | 1 + drivers/media/pci/smipcie/smipcie-ir.c | 1 + drivers/media/pci/ttpci/budget-ci.c | 1 + drivers/media/rc/ati_remote.c | 6 +++--- drivers/media/rc/ene_ir.c | 2 +- drivers/media/rc/fintek-cir.c | 3 ++- drivers/media/rc/igorplugusb.c | 1 + drivers/media/rc/iguanair.c | 1 + drivers/media/rc/img-ir/img-ir-hw.c | 3 ++- drivers/media/rc/img-ir/img-ir-raw.c | 3 ++- drivers/media/rc/imon.c | 3 ++- drivers/media/rc/ir-hix5hd2.c | 2 +- drivers/media/rc/ir_toy.c | 1 + drivers/media/rc/ite-cir.c | 2 +- drivers/media/rc/mceusb.c | 1 + drivers/media/rc/rc-ir-raw.c | 5 ----- drivers/media/rc/rc-loopback.c | 1 + drivers/media/rc/rc-main.c | 6 +----- drivers/media/rc/redrat3.c | 4 +++- drivers/media/rc/st_rc.c | 2 +- drivers/media/rc/streamzap.c | 7 ++++--- drivers/media/rc/sunxi-cir.c | 1 + drivers/media/rc/ttusbir.c | 2 +- drivers/media/rc/winbond-cir.c | 2 +- drivers/media/rc/xbox_remote.c | 5 +++-- drivers/media/usb/au0828/au0828-input.c | 1 + drivers/media/usb/dvb-usb-v2/dvb_usb_core.c | 1 + drivers/media/usb/dvb-usb/dvb-usb-remote.c | 6 ++++-- drivers/media/usb/em28xx/em28xx-input.c | 1 + drivers/staging/media/av7110/av7110_ir.c | 1 + include/media/rc-core.h | 2 -- 41 files changed, 58 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 9e48ad39e1cc..923e2ed30624 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2221,6 +2221,7 @@ static void sii8620_detach(struct drm_bridge *bridge) return; rc_unregister_device(ctx->rc_dev); + rc_free_device(ctx->rc_dev); } static int sii8620_is_packing_required(struct sii8620 *ctx, diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index d6faa0e00f95..6d4c636e1c9f 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -134,5 +134,6 @@ void picolcd_exit_cir(struct picolcd_data *data) data->rc_dev = NULL; rc_unregister_device(rdev); + rc_free_device(rdev); } diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index dd6e24a0899b..1b8a33c05b3c 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -338,8 +338,8 @@ int cec_register_adapter(struct cec_adapter *adap, res = cec_devnode_register(&adap->devnode, adap->owner); if (res) { #ifdef CONFIG_MEDIA_CEC_RC - /* Note: rc_unregister also calls rc_free */ rc_unregister_device(adap->rc); + rc_free_device(adap->rc); adap->rc = NULL; #endif return res; diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c index d85c78c104b9..5f4c0aa7a0d7 100644 --- a/drivers/media/common/siano/smsir.c +++ b/drivers/media/common/siano/smsir.c @@ -92,6 +92,7 @@ int sms_ir_init(struct smscore_device_t *coredev) void sms_ir_exit(struct smscore_device_t *coredev) { rc_unregister_device(coredev->ir.dev); + rc_free_device(coredev->ir.dev); pr_debug("\n"); } diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c index 5588cdd7ec20..604745317004 100644 --- a/drivers/media/i2c/ir-kbd-i2c.c +++ b/drivers/media/i2c/ir-kbd-i2c.c @@ -355,6 +355,7 @@ static void ir_work(struct work_struct *work) mutex_unlock(&ir->lock); if (rc == -ENODEV) { rc_unregister_device(ir->rc); + rc_free_device(ir->rc); ir->rc = NULL; return; } @@ -972,6 +973,7 @@ static void ir_remove(struct i2c_client *client) i2c_unregister_device(ir->tx_c); rc_unregister_device(ir->rc); + rc_free_device(ir->rc); } static const struct i2c_device_id ir_kbd_id[] = { diff --git a/drivers/media/pci/bt8xx/bttv-input.c b/drivers/media/pci/bt8xx/bttv-input.c index 84aa269248fd..f84fcf96eca9 100644 --- a/drivers/media/pci/bt8xx/bttv-input.c +++ b/drivers/media/pci/bt8xx/bttv-input.c @@ -572,8 +572,9 @@ void bttv_input_fini(struct bttv *btv) if (btv->remote == NULL) return; - bttv_ir_stop(btv); rc_unregister_device(btv->remote->dev); + bttv_ir_stop(btv); + rc_free_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; } diff --git a/drivers/media/pci/cx23885/cx23885-input.c b/drivers/media/pci/cx23885/cx23885-input.c index d2e84c6457e0..722329ef3fd2 100644 --- a/drivers/media/pci/cx23885/cx23885-input.c +++ b/drivers/media/pci/cx23885/cx23885-input.c @@ -402,6 +402,7 @@ void cx23885_input_fini(struct cx23885_dev *dev) if (dev->kernel_ir == NULL) return; rc_unregister_device(dev->kernel_ir->rc); + rc_free_device(dev->kernel_ir->rc); kfree(dev->kernel_ir->phys); kfree(dev->kernel_ir->name); kfree(dev->kernel_ir); diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c index b9f2c14d62b4..4757787c3f59 100644 --- a/drivers/media/pci/cx88/cx88-input.c +++ b/drivers/media/pci/cx88/cx88-input.c @@ -509,8 +509,9 @@ int cx88_ir_fini(struct cx88_core *core) if (!ir) return 0; - cx88_ir_stop(core); rc_unregister_device(ir->dev); + cx88_ir_stop(core); + rc_free_device(ir->dev); kfree(ir); /* done */ diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 9e9c7c071acc..e1185aa669f4 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -763,6 +763,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105) static void dm1105_ir_exit(struct dm1105_dev *dm1105) { rc_unregister_device(dm1105->ir.dev); + rc_free_device(dm1105->ir.dev); } static int dm1105_hw_init(struct dm1105_dev *dev) diff --git a/drivers/media/pci/mantis/mantis_input.c b/drivers/media/pci/mantis/mantis_input.c index 34c0d979240f..edb4cacf55d2 100644 --- a/drivers/media/pci/mantis/mantis_input.c +++ b/drivers/media/pci/mantis/mantis_input.c @@ -72,5 +72,6 @@ EXPORT_SYMBOL_GPL(mantis_input_init); void mantis_input_exit(struct mantis_pci *mantis) { rc_unregister_device(mantis->rc); + rc_free_device(mantis->rc); } EXPORT_SYMBOL_GPL(mantis_input_exit); diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c index 468dbe8d552f..d39537c95d9d 100644 --- a/drivers/media/pci/saa7134/saa7134-input.c +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -834,6 +834,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; rc_unregister_device(dev->remote->dev); + rc_free_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; } diff --git a/drivers/media/pci/smipcie/smipcie-ir.c b/drivers/media/pci/smipcie/smipcie-ir.c index c0604d9c7011..0bbe4fa2d5a8 100644 --- a/drivers/media/pci/smipcie/smipcie-ir.c +++ b/drivers/media/pci/smipcie/smipcie-ir.c @@ -181,5 +181,6 @@ void smi_ir_exit(struct smi_dev *dev) rc_unregister_device(rc_dev); smi_ir_stop(ir); + rc_free_device(rc_dev); ir->rc_dev = NULL; } diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 33f08adf4feb..16973ac8e6a9 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -249,6 +249,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work); rc_unregister_device(budget_ci->ir.dev); + rc_free_device(budget_ci->ir.dev); } static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address) diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index a733914a2574..f1fd4765651c 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -921,7 +921,6 @@ static int ati_remote_probe(struct usb_interface *interface, input_free_device(input_dev); exit_unregister_device: rc_unregister_device(rc_dev); - rc_dev = NULL; exit_kill_urbs: usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); @@ -941,18 +940,19 @@ static void ati_remote_disconnect(struct usb_interface *interface) struct ati_remote *ati_remote; ati_remote = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); if (!ati_remote) { dev_warn(&interface->dev, "%s - null device?\n", __func__); return; } + rc_unregister_device(ati_remote->rdev); + usb_set_intfdata(interface, NULL); usb_kill_urb(ati_remote->irq_urb); usb_kill_urb(ati_remote->out_urb); if (ati_remote->idev) input_unregister_device(ati_remote->idev); - rc_unregister_device(ati_remote->rdev); ati_remote_free_buffers(ati_remote); + rc_free_device(ati_remote->rdev); kfree(ati_remote); } diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index d6c54a3bccc2..136fc4192265 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -1090,7 +1090,6 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) release_region(dev->hw_io, ENE_IO_SIZE); exit_unregister_device: rc_unregister_device(rdev); - rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(dev); @@ -1110,6 +1109,7 @@ static void ene_remove(struct pnp_dev *pnp_dev) ene_rx_restore_hw_buffer(dev); spin_unlock_irqrestore(&dev->hw_lock, flags); + rc_free_device(dev->rdev); free_irq(dev->irq, dev); release_region(dev->hw_io, ENE_IO_SIZE); kfree(dev); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 3fb0968efd57..9b789097cdd4 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -568,6 +568,7 @@ static void fintek_remove(struct pnp_dev *pdev) struct fintek_dev *fintek = pnp_get_drvdata(pdev); unsigned long flags; + rc_unregister_device(fintek->rdev); spin_lock_irqsave(&fintek->fintek_lock, flags); /* disable CIR */ fintek_disable_cir(fintek); @@ -580,7 +581,7 @@ static void fintek_remove(struct pnp_dev *pdev) free_irq(fintek->cir_irq, fintek); release_region(fintek->cir_addr, fintek->cir_port_len); - rc_unregister_device(fintek->rdev); + rc_free_device(fintek->rdev); kfree(fintek); } diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index e7e31776453c..439f95e6879f 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -247,6 +247,7 @@ static void igorplugusb_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_unpoison_urb(ir->urb); usb_free_urb(ir->urb); + rc_free_device(ir->rc); kfree(ir->buf_in); kfree(ir->request); } diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c index 8af94246e591..7bd6dd725415 100644 --- a/drivers/media/rc/iguanair.c +++ b/drivers/media/rc/iguanair.c @@ -500,6 +500,7 @@ static void iguanair_disconnect(struct usb_interface *intf) usb_set_intfdata(intf, NULL); usb_kill_urb(ir->urb_in); usb_kill_urb(ir->urb_out); + rc_free_device(ir->rc); usb_free_urb(ir->urb_in); usb_free_urb(ir->urb_out); usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in); diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 63f6f5b36838..f30adf4d8444 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -1118,9 +1118,10 @@ void img_ir_remove_hw(struct img_ir_priv *priv) struct rc_dev *rdev = hw->rdev; if (!rdev) return; + rc_unregister_device(rdev); img_ir_set_decoder(priv, NULL, 0); hw->rdev = NULL; - rc_unregister_device(rdev); + rc_free_device(rdev); #ifdef CONFIG_COMMON_CLK if (!IS_ERR(priv->clk)) clk_notifier_unregister(priv->clk, &hw->clk_nb); diff --git a/drivers/media/rc/img-ir/img-ir-raw.c b/drivers/media/rc/img-ir/img-ir-raw.c index 92fb7b555a0f..f1460d4acf3e 100644 --- a/drivers/media/rc/img-ir/img-ir-raw.c +++ b/drivers/media/rc/img-ir/img-ir-raw.c @@ -136,6 +136,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) if (!rdev) return; + rc_unregister_device(rdev); /* switch off and disable raw (edge) interrupts */ spin_lock_irq(&priv->lock); raw->rdev = NULL; @@ -145,7 +146,7 @@ void img_ir_remove_raw(struct img_ir_priv *priv) img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_EDGE); spin_unlock_irq(&priv->lock); - rc_unregister_device(rdev); + rc_free_device(rdev); timer_delete_sync(&raw->timer); } diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 35b9e07003d8..48534bb52e4d 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -2541,9 +2541,10 @@ static void imon_disconnect(struct usb_interface *interface) if (ifnum == 0) { ictx->dev_present_intf0 = false; + rc_unregister_device(ictx->rdev); usb_kill_urb(ictx->rx_urb_intf0); input_unregister_device(ictx->idev); - rc_unregister_device(ictx->rdev); + rc_free_device(ictx->rdev); if (ictx->display_supported) { if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) usb_deregister_dev(interface, &imon_lcd_class); diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c index afd80d2350c6..bb0f95833df5 100644 --- a/drivers/media/rc/ir-hix5hd2.c +++ b/drivers/media/rc/ir-hix5hd2.c @@ -331,7 +331,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev) regerr: rc_unregister_device(rdev); - rdev = NULL; clkerr: clk_disable_unprepare(priv->clock); err: @@ -346,6 +345,7 @@ static void hix5hd2_ir_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clock); rc_unregister_device(priv->rdev); + rc_free_device(priv->rdev); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c index 533faa117517..e79de56997a4 100644 --- a/drivers/media/rc/ir_toy.c +++ b/drivers/media/rc/ir_toy.c @@ -536,6 +536,7 @@ static void irtoy_disconnect(struct usb_interface *intf) usb_free_urb(ir->urb_out); usb_kill_urb(ir->urb_in); usb_free_urb(ir->urb_in); + rc_free_device(ir->rc); kfree(ir->in); kfree(ir->out); kfree(ir); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 2bacecb02262..23afbafb5574 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1414,7 +1414,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id release_region(itdev->cir_addr, itdev->params->io_region_size); exit_unregister_device: rc_unregister_device(rdev); - rdev = NULL; exit_free_dev_rdev: rc_free_device(rdev); kfree(itdev); @@ -1439,6 +1438,7 @@ static void ite_remove(struct pnp_dev *pdev) release_region(dev->cir_addr, dev->params->io_region_size); rc_unregister_device(dev->rdev); + rc_free_device(dev->rdev); kfree(dev); } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 044767eb3a38..a4c94fdf767c 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1850,6 +1850,7 @@ static void mceusb_dev_disconnect(struct usb_interface *intf) usb_free_urb(ir->urb_in); usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); usb_put_dev(dev); + rc_free_device(ir->rc); kfree(ir); } diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index 5dafe11f61c6..76c3d1307f9f 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -648,9 +648,6 @@ int ir_raw_event_register(struct rc_dev *dev) void ir_raw_event_free(struct rc_dev *dev) { - if (!dev) - return; - kfree(dev->raw); dev->raw = NULL; } @@ -674,8 +671,6 @@ void ir_raw_event_unregister(struct rc_dev *dev) lirc_bpf_free(dev); - ir_raw_event_free(dev); - /* * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so * ensure that the raw member is null on unlock; this is how diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index 8288366f891f..a108b057b5fd 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -263,6 +263,7 @@ static int __init loop_init(void) static void __exit loop_exit(void) { rc_unregister_device(loopdev.dev); + rc_free_device(loopdev.dev); } module_init(loop_init); diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index b9bf5cdcde4a..6bdf32cb4a17 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1611,6 +1611,7 @@ static void rc_dev_release(struct device *device) { struct rc_dev *dev = to_rc_dev(device); + ir_raw_event_free(dev); kfree(dev); } @@ -1773,7 +1774,6 @@ struct rc_dev *devm_rc_allocate_device(struct device *dev, } rc->dev.parent = dev; - rc->managed_alloc = true; *dr = rc; devres_add(dev, dr); @@ -2042,11 +2042,7 @@ void rc_unregister_device(struct rc_dev *dev) device_del(&dev->dev); ida_free(&rc_ida, dev->minor); - - if (!dev->managed_alloc) - rc_free_device(dev); } - EXPORT_SYMBOL_GPL(rc_unregister_device); /* diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index a49173f54a4d..b8289327f6a2 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -1133,11 +1133,13 @@ static void redrat3_dev_disconnect(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct redrat3_dev *rr3 = usb_get_intfdata(intf); + struct rc_dev *rc = rr3->rc; usb_set_intfdata(intf, NULL); - rc_unregister_device(rr3->rc); + rc_unregister_device(rc); led_classdev_unregister(&rr3->led); redrat3_delete(rr3, udev); + rc_free_device(rc); } static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message) diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c index 6b70bac5f45d..0ba06bfc9e14 100644 --- a/drivers/media/rc/st_rc.c +++ b/drivers/media/rc/st_rc.c @@ -203,6 +203,7 @@ static void st_rc_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); clk_disable_unprepare(rc_dev->sys_clock); rc_unregister_device(rc_dev->rdev); + rc_free_device(rc_dev->rdev); } static int st_rc_open(struct rc_dev *rdev) @@ -334,7 +335,6 @@ static int st_rc_probe(struct platform_device *pdev) return ret; rcerr: rc_unregister_device(rdev); - rdev = NULL; clkerr: clk_disable_unprepare(rc_dev->sys_clock); err: diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index 8e9b156e4300..8c85b9f30a3a 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -392,15 +392,16 @@ static void streamzap_disconnect(struct usb_interface *interface) struct streamzap_ir *sz = usb_get_intfdata(interface); struct usb_device *usbdev = interface_to_usbdev(interface); - usb_set_intfdata(interface, NULL); - if (!sz) return; - usb_kill_urb(sz->urb_in); rc_unregister_device(sz->rdev); + usb_set_intfdata(interface, NULL); + + usb_kill_urb(sz->urb_in); usb_free_urb(sz->urb_in); usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in); + rc_free_device(sz->rdev); kfree(sz); } diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 92ef4e7c6f69..cb4c56bf0752 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -371,6 +371,7 @@ static void sunxi_ir_remove(struct platform_device *pdev) struct sunxi_ir *ir = platform_get_drvdata(pdev); rc_unregister_device(ir->rc); + rc_free_device(ir->rc); sunxi_ir_hw_exit(&pdev->dev); } diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index dde446a95eaa..a670d4b008cb 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -336,7 +336,6 @@ static int ttusbir_probe(struct usb_interface *intf, return 0; out3: rc_unregister_device(rc); - rc = NULL; out2: led_classdev_unregister(&tt->led); out: @@ -378,6 +377,7 @@ static void ttusbir_disconnect(struct usb_interface *intf) usb_kill_urb(tt->bulk_urb); usb_free_urb(tt->bulk_urb); kfree(tt->bulk_buffer); + rc_free_device(tt->rc); usb_set_intfdata(intf, NULL); kfree(tt); } diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 25884a79985c..14d8b58e2839 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1132,7 +1132,6 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id) release_region(data->wbase, WAKEUP_IOMEM_LEN); exit_unregister_device: rc_unregister_device(data->dev); - data->dev = NULL; exit_free_rc: rc_free_device(data->dev); exit_unregister_led: @@ -1163,6 +1162,7 @@ wbcir_remove(struct pnp_dev *device) wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_EV_EN, 0x00, 0x07); rc_unregister_device(data->dev); + rc_free_device(data->dev); led_classdev_unregister(&data->led); diff --git a/drivers/media/rc/xbox_remote.c b/drivers/media/rc/xbox_remote.c index 0c9c855ced72..80b7c247932a 100644 --- a/drivers/media/rc/xbox_remote.c +++ b/drivers/media/rc/xbox_remote.c @@ -283,14 +283,15 @@ static void xbox_remote_disconnect(struct usb_interface *interface) struct xbox_remote *xbox_remote; xbox_remote = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); if (!xbox_remote) { dev_warn(&interface->dev, "%s - null device?\n", __func__); return; } - usb_kill_urb(xbox_remote->irq_urb); rc_unregister_device(xbox_remote->rdev); + usb_set_intfdata(interface, NULL); + usb_kill_urb(xbox_remote->irq_urb); + rc_free_device(xbox_remote->rdev); usb_free_urb(xbox_remote->irq_urb); kfree(xbox_remote->inbuf); kfree(xbox_remote); diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index 3d3368202cd0..283ad2c6288c 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -357,6 +357,7 @@ void au0828_rc_unregister(struct au0828_dev *dev) return; rc_unregister_device(ir->rc); + rc_free_device(ir->rc); /* done */ kfree(ir); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index f1c79f351ec8..17e8961179d1 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -187,6 +187,7 @@ static int dvb_usbv2_remote_exit(struct dvb_usb_device *d) if (d->rc_dev) { cancel_delayed_work_sync(&d->rc_query_work); rc_unregister_device(d->rc_dev); + rc_free_device(d->rc_dev); d->rc_dev = NULL; } diff --git a/drivers/media/usb/dvb-usb/dvb-usb-remote.c b/drivers/media/usb/dvb-usb/dvb-usb-remote.c index 65e2c9e2cdc9..6dc11718dfb9 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-remote.c @@ -347,10 +347,12 @@ int dvb_usb_remote_exit(struct dvb_usb_device *d) { if (d->state & DVB_USB_STATE_REMOTE) { cancel_delayed_work_sync(&d->rc_query_work); - if (d->props.rc.mode == DVB_RC_LEGACY) + if (d->props.rc.mode == DVB_RC_LEGACY) { input_unregister_device(d->input_dev); - else + } else { rc_unregister_device(d->rc_dev); + rc_free_device(d->rc_dev); + } } d->state &= ~DVB_USB_STATE_REMOTE; return 0; diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 5f3b00869bdb..26f333b5be73 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -853,6 +853,7 @@ static int em28xx_ir_fini(struct em28xx *dev) goto ref_put; rc_unregister_device(ir->rc); + rc_free_device(ir->rc); kfree(ir->i2c_client); diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c index 68b3979ba5f2..fdae467fd7ab 100644 --- a/drivers/staging/media/av7110/av7110_ir.c +++ b/drivers/staging/media/av7110/av7110_ir.c @@ -151,6 +151,7 @@ int av7110_ir_init(struct av7110 *av7110) void av7110_ir_exit(struct av7110 *av7110) { rc_unregister_device(av7110->ir.rcdev); + rc_free_device(av7110->ir.rcdev); } //MODULE_AUTHOR("Holger Waechtler , Oliver Endriss "); diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 35c7a0546f02..7c964b5ad792 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -81,7 +81,6 @@ struct lirc_fh { /** * struct rc_dev - represents a remote control device * @dev: driver model's view of this device - * @managed_alloc: devm_rc_allocate_device was used to create rc_dev * @registered: set to true by rc_register_device(), false by * rc_unregister_device * @idle: used to keep track of RX state @@ -156,7 +155,6 @@ struct lirc_fh { */ struct rc_dev { struct device dev; - bool managed_alloc; bool registered; bool idle; bool encode_wakeup; From efacf63ed087050a4091298427bfb69e34082ef1 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 10 Apr 2026 23:03:09 +0200 Subject: [PATCH 474/697] media: rc: ttusbir: fix inverted error logic [ Upstream commit 646ebdd3105809d84ed04aa9e92e47e89cc44502 ] We have to report ENOMEM if no buffer is allocated. Typo dropped a "!". Restore it. Fixes: 50acaad3d202 ("media: rc: ttusbir: respect DMA coherency rules") Cc: stable@vger.kernel.org Signed-off-by: Oliver Neukum Signed-off-by: Sean Young Signed-off-by: Hans Verkuil Signed-off-by: Sasha Levin --- drivers/media/rc/ttusbir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/ttusbir.c b/drivers/media/rc/ttusbir.c index a670d4b008cb..3452b5aefd28 100644 --- a/drivers/media/rc/ttusbir.c +++ b/drivers/media/rc/ttusbir.c @@ -191,7 +191,7 @@ static int ttusbir_probe(struct usb_interface *intf, tt = kzalloc(sizeof(*tt), GFP_KERNEL); buffer = kzalloc(5, GFP_KERNEL); rc = rc_allocate_device(RC_DRIVER_IR_RAW); - if (!tt || !rc || buffer) { + if (!tt || !rc || !buffer) { ret = -ENOMEM; goto out; } From ff0ca46b13b9ef6edbcd238a3b6caacfef8ba0e5 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 20:11:31 -0400 Subject: [PATCH 475/697] smb: client: validate the whole DACL before rewriting it in cifsacl [ Upstream commit 0a8cf165566ba55a39fd0f4de172119dd646d39a ] build_sec_desc() and id_mode_to_cifs_acl() derive a DACL pointer from a server-supplied dacloffset and then use the incoming ACL to rebuild the chmod/chown security descriptor. The original fix only checked that the struct smb_acl header fits before reading dacl_ptr->size or dacl_ptr->num_aces. That avoids the immediate header-field OOB read, but the rewrite helpers still walk ACEs based on pdacl->num_aces with no structural validation of the incoming DACL body. A malicious server can return a truncated DACL that still contains a header, claims one or more ACEs, and then drive replace_sids_and_copy_aces() or set_chmod_dacl() past the validated extent while they compare or copy attacker-controlled ACEs. Factor the DACL structural checks into validate_dacl(), extend them to validate each ACE against the DACL bounds, and use the shared validator before the chmod/chown rebuild paths. parse_dacl() reuses the same validator so the read-side parser and write-side rewrite paths agree on what constitutes a well-formed incoming DACL. Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Steve French Signed-off-by: Sasha Levin --- fs/smb/client/cifsacl.c | 116 +++++++++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 31 deletions(-) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index e29488355870..d17912beb28e 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -757,6 +757,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl) } #endif +static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl) +{ + int i, ace_hdr_size, ace_size, min_ace_size; + u16 dacl_size, num_aces; + char *acl_base, *end_of_dacl; + struct smb_ace *pace; + + if (!pdacl) + return 0; + + if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + dacl_size = le16_to_cpu(pdacl->size); + if (dacl_size < sizeof(struct smb_acl) || + end_of_acl < (char *)pdacl + dacl_size) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + num_aces = le16_to_cpu(pdacl->num_aces); + if (!num_aces) + return 0; + + ace_hdr_size = offsetof(struct smb_ace, sid) + + offsetof(struct smb_sid, sub_auth); + min_ace_size = ace_hdr_size + sizeof(__le32); + if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) { + cifs_dbg(VFS, "ACL too small to parse DACL\n"); + return -EINVAL; + } + + end_of_dacl = (char *)pdacl + dacl_size; + acl_base = (char *)pdacl; + ace_size = sizeof(struct smb_acl); + + for (i = 0; i < num_aces; ++i) { + if (end_of_dacl - acl_base < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + pace = (struct smb_ace *)(acl_base + ace_size); + acl_base = (char *)pace; + + if (end_of_dacl - acl_base < ace_hdr_size || + pace->sid.num_subauth == 0 || + pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; + if (end_of_dacl - acl_base < ace_size || + le16_to_cpu(pace->size) < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + + ace_size = le16_to_cpu(pace->size); + if (end_of_dacl - acl_base < ace_size) { + cifs_dbg(VFS, "ACL too small to parse ACE\n"); + return -EINVAL; + } + } + + return 0; +} + static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, struct smb_sid *pownersid, struct smb_sid *pgrpsid, struct cifs_fattr *fattr, bool mode_from_special_sid) @@ -764,7 +835,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, int i; u16 num_aces = 0; int acl_size; - char *acl_base; + char *acl_base, *end_of_dacl; struct smb_ace **ppace; /* BB need to add parm so we can store the SID BB */ @@ -776,12 +847,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, return; } - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || - end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { - cifs_dbg(VFS, "ACL too small to parse DACL\n"); + if (validate_dacl(pdacl, end_of_acl)) return; - } cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), @@ -792,6 +859,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, user/group/other have no permissions */ fattr->cf_mode &= ~(0777); + end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size); acl_base = (char *)pdacl; acl_size = sizeof(struct smb_acl); @@ -799,36 +867,16 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, if (num_aces > 0) { umode_t denied_mode = 0; - if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / - (offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) - return; - ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), GFP_KERNEL); if (!ppace) return; for (i = 0; i < num_aces; ++i) { - if (end_of_acl - acl_base < acl_size) - break; - ppace[i] = (struct smb_ace *) (acl_base + acl_size); - acl_base = (char *)ppace[i]; - acl_size = offsetof(struct smb_ace, sid) + - offsetof(struct smb_sid, sub_auth); - - if (end_of_acl - acl_base < acl_size || - ppace[i]->sid.num_subauth == 0 || - ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || - (end_of_acl - acl_base < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || - (le16_to_cpu(ppace[i]->size) < - acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) - break; #ifdef CONFIG_CIFS_DEBUG2 - dump_ace(ppace[i], end_of_acl); + dump_ace(ppace[i], end_of_dacl); #endif if (mode_from_special_sid && ppace[i]->sid.num_subauth >= 3 && @@ -871,6 +919,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, (void *)ppace[i], sizeof(struct smb_ace)); */ + acl_base = (char *)ppace[i]; acl_size = le16_to_cpu(ppace[i]->size); } @@ -1316,10 +1365,9 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, } dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); - if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { - cifs_dbg(VFS, "Server returned illegal ACL size\n"); - return -EINVAL; - } + rc = validate_dacl(dacl_ptr, end_of_acl); + if (rc) + return rc; } owner_sid_ptr = (struct smb_sid *)((char *)pntsd + @@ -1698,6 +1746,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, } dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); + if (rc) { + kfree(pntsd); + cifs_put_tlink(tlink); + return rc; + } if (mode_from_sid) nsecdesclen += le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); From 0171e01de47a4ccb2c64c668871a01c99d82d740 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Tue, 26 May 2026 20:50:43 +0000 Subject: [PATCH 476/697] Revert "x86/fpu: Refine and simplify the magic number check during signal return" [ Upstream commit 44eeff9bc467bc7d1fec34fc3f6001f385fe462c ] This reverts dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return"). The aforementioned commit broke applications that construct signal frames in userspace (such as CRIU and gVisor) if the frame's xstate size is smaller than the kernel's fpstate->user_size. Furthermore, this introduces a critical issue for checkpoint/restore tools like CRIU. If a process is checkpointed while inside a signal handler, its stack contains a signal frame formatted according to the source host's xstate capabilities. If that process is later restored on a destination host with larger xstate capabilities (e.g., a newer CPU with more features enabled, resulting in a larger fpstate->user_size), the kernel will look for FP_XSTATE_MAGIC2 at the destination host's larger user_size offset instead of the offset encoded in the frame's fx_sw->xstate_size. This causes the magic2 check to fail, forcing sigreturn to silently fall back to "FX-only" mode. Upon return from the signal handler, the process's extended state is reset to initial values instead of being restored, leading to silent data corruption. The aforementioned commit cited d877550eaf2d ("x86/fpu: Stop relying on userspace for info to fault in xsave buffer") as justification to stop relying on userspace for the magic number check. However, these two changes are fundamentally different. The last one only changed how much memory the kernel ensures is paged-in before running XRSTOR to prevent an infinite loop. It did not change the signal frame format or how the layout is validated. Reverting this change restores the use of fx_sw->xstate_size for locating magic2 and restores the necessary sanity checks, ensuring that the signal frame remains self-describing and portable. [ bp: Massage commit message. ] Fixes: dc8aa31a7ac2 ("x86/fpu: Refine and simplify the magic number check during signal return") Signed-off-by: Andrei Vagin Signed-off-by: Borislav Petkov (AMD) Acked-by: Chang S. Bae Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20260429000623.3356606-1-avagin@google.com Signed-off-by: Sasha Levin --- arch/x86/kernel/fpu/signal.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c index c3ec2512f2bb..20b638c507ca 100644 --- a/arch/x86/kernel/fpu/signal.c +++ b/arch/x86/kernel/fpu/signal.c @@ -27,14 +27,19 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, struct _fpx_sw_bytes *fx_sw) { + int min_xstate_size = sizeof(struct fxregs_state) + + sizeof(struct xstate_header); void __user *fpstate = fxbuf; unsigned int magic2; if (__copy_from_user(fx_sw, &fxbuf->sw_reserved[0], sizeof(*fx_sw))) return false; - /* Check for the first magic field */ - if (fx_sw->magic1 != FP_XSTATE_MAGIC1) + /* Check for the first magic field and other error scenarios. */ + if (fx_sw->magic1 != FP_XSTATE_MAGIC1 || + fx_sw->xstate_size < min_xstate_size || + fx_sw->xstate_size > x86_task_fpu(current)->fpstate->user_size || + fx_sw->xstate_size > fx_sw->extended_size) goto setfx; /* @@ -43,7 +48,7 @@ static inline bool check_xstate_in_sigframe(struct fxregs_state __user *fxbuf, * fpstate layout with out copying the extended state information * in the memory layout. */ - if (__get_user(magic2, (__u32 __user *)(fpstate + x86_task_fpu(current)->fpstate->user_size))) + if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))) return false; if (likely(magic2 == FP_XSTATE_MAGIC2)) From 600ad63124deada4ed039cd2949b0705e93bb640 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Tue, 2 Jun 2026 18:22:21 +0200 Subject: [PATCH 477/697] s390/cio: Restore GFP_DMA for CHSC allocation [ Upstream commit ea34567db0a6b3a7ce78ba421592344315c8f90e ] Re-add GFP_DMA when allocating memory for CHSC control blocks. On some supported machines, CHSC cannot access memory outside the DMA zone, causing CHSC command failures. Cc: stable@vger.kernel.org Fixes: a3a64a4def8d ("s390/cio: remove unneeded DMA zone allocation") Signed-off-by: Peter Oberparleiter Reviewed-by: Heiko Carstens Signed-off-by: Alexander Gordeev [ adjusted context to account for missing commit bf4afc53b77ae ] Signed-off-by: Peter Oberparleiter Signed-off-by: Sasha Levin --- drivers/s390/cio/chsc.c | 4 ++-- drivers/s390/cio/chsc_sch.c | 20 ++++++++++---------- drivers/s390/cio/scm.c | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 239c92d4ec11..b5f6eb18ebad 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -1143,8 +1143,8 @@ int __init chsc_init(void) { int ret; - sei_page = (void *)get_zeroed_page(GFP_KERNEL); - chsc_page = (void *)get_zeroed_page(GFP_KERNEL); + sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sei_page || !chsc_page) { ret = -ENOMEM; goto out_err; diff --git a/drivers/s390/cio/chsc_sch.c b/drivers/s390/cio/chsc_sch.c index 1e58ee3cc87d..9131ce3af1b8 100644 --- a/drivers/s390/cio/chsc_sch.c +++ b/drivers/s390/cio/chsc_sch.c @@ -293,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area) if (!css_general_characteristics.dynio) /* It makes no sense to try. */ return -EOPNOTSUPP; - chsc_area = (void *)get_zeroed_page(GFP_KERNEL); + chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); if (!chsc_area) return -ENOMEM; request = kzalloc(sizeof(*request), GFP_KERNEL); @@ -341,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area) ret = -ENOMEM; goto out_unlock; } - on_close_chsc_area = (void *)get_zeroed_page(GFP_KERNEL); + on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL); if (!on_close_chsc_area) { ret = -ENOMEM; goto out_free_request; @@ -393,7 +393,7 @@ static int chsc_ioctl_start_sync(void __user *user_area) struct chsc_sync_area *chsc_area; int ret, ccode; - chsc_area = (void *)get_zeroed_page(GFP_KERNEL); + chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!chsc_area) return -ENOMEM; if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) { @@ -439,7 +439,7 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd) u8 data[PAGE_SIZE - 20]; } __attribute__ ((packed)) *scpcd_area; - scpcd_area = (void *)get_zeroed_page(GFP_KERNEL); + scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scpcd_area) return -ENOMEM; cd = kzalloc(sizeof(*cd), GFP_KERNEL); @@ -501,7 +501,7 @@ static int chsc_ioctl_info_cu(void __user *user_cd) u8 data[PAGE_SIZE - 20]; } __attribute__ ((packed)) *scucd_area; - scucd_area = (void *)get_zeroed_page(GFP_KERNEL); + scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scucd_area) return -ENOMEM; cd = kzalloc(sizeof(*cd), GFP_KERNEL); @@ -564,7 +564,7 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud) u8 data[PAGE_SIZE - 20]; } __attribute__ ((packed)) *sscud_area; - sscud_area = (void *)get_zeroed_page(GFP_KERNEL); + sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sscud_area) return -ENOMEM; cud = kzalloc(sizeof(*cud), GFP_KERNEL); @@ -626,7 +626,7 @@ static int chsc_ioctl_conf_info(void __user *user_ci) u8 data[PAGE_SIZE - 20]; } __attribute__ ((packed)) *sci_area; - sci_area = (void *)get_zeroed_page(GFP_KERNEL); + sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sci_area) return -ENOMEM; ci = kzalloc(sizeof(*ci), GFP_KERNEL); @@ -697,7 +697,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl) u32 res; } __attribute__ ((packed)) *cssids_parm; - sccl_area = (void *)get_zeroed_page(GFP_KERNEL); + sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sccl_area) return -ENOMEM; ccl = kzalloc(sizeof(*ccl), GFP_KERNEL); @@ -757,7 +757,7 @@ static int chsc_ioctl_chpd(void __user *user_chpd) int ret; chpd = kzalloc(sizeof(*chpd), GFP_KERNEL); - scpd_area = (void *)get_zeroed_page(GFP_KERNEL); + scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!scpd_area || !chpd) { ret = -ENOMEM; goto out_free; @@ -797,7 +797,7 @@ static int chsc_ioctl_dcal(void __user *user_dcal) u8 data[PAGE_SIZE - 36]; } __attribute__ ((packed)) *sdcal_area; - sdcal_area = (void *)get_zeroed_page(GFP_KERNEL); + sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); if (!sdcal_area) return -ENOMEM; dcal = kzalloc(sizeof(*dcal), GFP_KERNEL); diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c index 9b4da237a0ed..f4faa38a4b17 100644 --- a/drivers/s390/cio/scm.c +++ b/drivers/s390/cio/scm.c @@ -229,7 +229,7 @@ int scm_update_information(void) size_t num; int ret; - scm_info = (void *)__get_free_page(GFP_KERNEL); + scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); if (!scm_info) return -ENOMEM; From dd4cbab2a446d87b94f8337e38653a0f9a679ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 29 May 2026 13:06:11 +0300 Subject: [PATCH 478/697] drm/i915/psr: Add defininitions for INTEL_WA_REGISTER_CAPS DPCD register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fbceb39b536e40c2f7cc47ab42037bb7c2b7ced9 upstream. EDP specification says: "If either VSC SDP is unable to be transmitted 100 ns before the SU region, the Source device may optionally transmit the VSC SDP during the prior video scan line’s HBlank period There is a Intel specific drm dp register currently containing bits related how TCON can support PSR2 with SDP on prior line." Unfortunately many panels are having problems in implementing this. So there is a custom Intel specific DPCD register (INTEL_WA_REGISTER_CAPS) to figure out if this is properly implemented on a panel or if panel doesn't require that 100 ns delay before the SU region. Here are the definitions in this custom DPCD address: 0 = Panel doesn't support SDP on prior line 1 = Panel supports SDP on prior line 2 = Panel doesn't have 100ns requirement 3 = Reserved Add definitions for this new register and it's values into new header intel_dpcd.h. v2: add INTEL_DPCD_ prefix to definitions Bspec: 74741 Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260515095756.2799483-2-jouni.hogander@intel.com (cherry picked from commit 1da1c9294825f08f622c473480d185680c2a3b75) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_dpcd.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 drivers/gpu/drm/i915/display/intel_dpcd.h diff --git a/drivers/gpu/drm/i915/display/intel_dpcd.h b/drivers/gpu/drm/i915/display/intel_dpcd.h new file mode 100644 index 000000000000..4aea5326f2ed --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dpcd.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef __INTEL_DPCD_H__ +#define __INTEL_DPCD_H__ + +#define INTEL_DPCD_INTEL_WA_REGISTER_CAPS 0x3f0 +# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK REG_GENMASK(1, 0) +# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1 0 +# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE 1 +# define INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE 2 + +#endif /* __INTEL_DPCD_H__ */ From c058cf6b84c131c3f535f5fd08ec6eba1e9da9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 29 May 2026 13:06:12 +0300 Subject: [PATCH 479/697] drm/i915/psr: Read Intel DPCD workaround register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f30bece421a4ae34359254e1dc2a187a42b6af9b upstream. Read Intel DPCD workaround register and store it into intel_connector->dp.psr_caps. psr_caps was chosen as currently it contains only PSR workaround for PSR2 SDP on prior scanline implementation. Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260515095756.2799483-3-jouni.hogander@intel.com (cherry picked from commit c48ff24d0f4ab7ad696b2d35ad64ce7e049c668c) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_psr.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 39dd7389f1a7..197bdb6592a7 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1710,6 +1710,7 @@ struct intel_dp { u8 lttpr_common_caps[DP_LTTPR_COMMON_CAP_SIZE]; u8 lttpr_phy_caps[DP_MAX_LTTPR_COUNT][DP_LTTPR_PHY_CAP_SIZE]; u8 pcon_dsc_dpcd[DP_PCON_DSC_ENCODER_CAP_SIZE]; + u8 intel_wa_dpcd; /* source rates */ int num_source_rates; const int *source_rates; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 1da20065ea77..802a671b2afa 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -41,6 +41,7 @@ #include "intel_display_types.h" #include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dpcd.h" #include "intel_dp_aux.h" #include "intel_dsb.h" #include "intel_frontbuffer.h" @@ -679,6 +680,12 @@ static void _psr_init_dpcd(struct intel_dp *intel_dp) drm_dbg_kms(display->drm, "PSR2 %ssupported\n", intel_dp->psr.sink_psr2_support ? "" : "not "); } + + if (intel_dp->psr.sink_psr2_support) + drm_dp_dpcd_read(&intel_dp->aux, + INTEL_DPCD_INTEL_WA_REGISTER_CAPS, + &intel_dp->intel_wa_dpcd, + sizeof(intel_dp->intel_wa_dpcd)); } void intel_psr_init_dpcd(struct intel_dp *intel_dp) From 487393023feb57ce5305e00eaae21dd50520f3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 29 May 2026 13:06:13 +0300 Subject: [PATCH 480/697] drm/i915/psr: Apply Intel DPCD workaround when SDP on prior line used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4703049f768fc1c1caac754134118bee1a3af189 upstream. There is Intel specific workaround DPCD address containing workaround for case where SDP is on prior line. Apply this workaround according to values in the offset. Fixes: 61e887329e33 ("drm/i915/xelpd: Handle PSR2 SDP indication in the prior scanline") Cc: # v5.15+ Signed-off-by: Jouni Högander Reviewed-by: Suraj Kandpal Link: https://patch.msgid.link/20260515095756.2799483-4-jouni.hogander@intel.com (cherry picked from commit c3fe899fbeac86ea4a5ca9dd845b2cbc0da46249) Signed-off-by: Tvrtko Ursulin Signed-off-by: Jouni Högander Signed-off-by: Sasha Levin --- drivers/gpu/drm/i915/display/intel_psr.c | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 802a671b2afa..eed281b93cd8 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -29,6 +29,7 @@ #include #include "i915_reg.h" +#include "i915_utils.h" #include "intel_alpm.h" #include "intel_atomic.h" #include "intel_crtc.h" @@ -1315,6 +1316,30 @@ static bool psr2_granularity_check(struct intel_dp *intel_dp, return true; } +static bool apply_scanline_indication_wa(struct intel_dp *intel_dp, + struct intel_crtc_state *crtc_state) +{ + u8 early_scanline_support = intel_dp->intel_wa_dpcd & + INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_EARLYSCANLINE_SDP_SUPPORT_MASK; + + if (intel_dp->edp_dpcd[0] >= DP_EDP_15) + return true; + + switch (early_scanline_support) { + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_FALL_BACK_TO_PSR1: + crtc_state->req_psr2_sdp_prior_scanline = false; + return false; + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITH_EARLY_SCANLINE: + return true; + case INTEL_DPCD_INTEL_WA_REGISTER_CAPS_PSR2_WITHOUT_EARLY_SCANLINE: + crtc_state->req_psr2_sdp_prior_scanline = false; + return true; + default: + MISSING_CASE(early_scanline_support); + return false; + } +} + static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { @@ -1336,7 +1361,8 @@ static bool _compute_psr2_sdp_prior_scanline_indication(struct intel_dp *intel_d return false; crtc_state->req_psr2_sdp_prior_scanline = true; - return true; + + return apply_scanline_indication_wa(intel_dp, crtc_state); } static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp, From 278b0df1f736faa30e18806edd9b4591a51f190b Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Thu, 23 Oct 2025 21:13:49 +0200 Subject: [PATCH 481/697] phy: mscc: Use PHY_ID_MATCH_EXACT for VSC8584, VSC8582, VSC8575, VSC856X [ Upstream commit 1bc80d673087e5704adbb3ee8e4b785c14899cce ] As the PHYs VSC8584, VSC8582, VSC8575 and VSC856X exists only as rev B, we can use PHY_ID_MATCH_EXACT to match exactly on revision B of the PHY. Because of this change then there is not need the check if it is a different revision than rev B in the function vsc8584_probe() as we already know that this will never happen. These changes are a preparation for the next patch because in that patch we will make the PHYs VSC8574 and VSC8572 to use vsc8584_probe() and these PHYs have multiple revision. Reviewed-by: Maxime Chevallier Signed-off-by: Horatiu Vultur Link: https://patch.msgid.link/20251023191350.190940-2-horatiu.vultur@microchip.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/mscc/mscc.h | 8 ++++---- drivers/net/phy/mscc/mscc_main.c | 23 ++++------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h index 2d8eca54c40a..2eef5956b9cc 100644 --- a/drivers/net/phy/mscc/mscc.h +++ b/drivers/net/phy/mscc/mscc.h @@ -289,12 +289,12 @@ enum rgmii_clock_delay { #define PHY_ID_VSC8540 0x00070760 #define PHY_ID_VSC8541 0x00070770 #define PHY_ID_VSC8552 0x000704e0 -#define PHY_ID_VSC856X 0x000707e0 +#define PHY_ID_VSC856X 0x000707e1 #define PHY_ID_VSC8572 0x000704d0 #define PHY_ID_VSC8574 0x000704a0 -#define PHY_ID_VSC8575 0x000707d0 -#define PHY_ID_VSC8582 0x000707b0 -#define PHY_ID_VSC8584 0x000707c0 +#define PHY_ID_VSC8575 0x000707d1 +#define PHY_ID_VSC8582 0x000707b1 +#define PHY_ID_VSC8584 0x000707c1 #define PHY_VENDOR_MSCC 0x00070400 #define MSCC_VDDMAC_1500 1500 diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c index 48d43f60b8ff..8678ebf89cca 100644 --- a/drivers/net/phy/mscc/mscc_main.c +++ b/drivers/net/phy/mscc/mscc_main.c @@ -1724,12 +1724,6 @@ static int vsc8584_config_init(struct phy_device *phydev) * in this pre-init function. */ if (phy_package_init_once(phydev)) { - /* The following switch statement assumes that the lowest - * nibble of the phy_id_mask is always 0. This works because - * the lowest nibble of the PHY_ID's below are also 0. - */ - WARN_ON(phydev->drv->phy_id_mask & 0xf); - switch (phydev->phy_id & phydev->drv->phy_id_mask) { case PHY_ID_VSC8504: case PHY_ID_VSC8552: @@ -2290,11 +2284,6 @@ static int vsc8584_probe(struct phy_device *phydev) VSC8531_DUPLEX_COLLISION}; int ret; - if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) { - dev_err(&phydev->mdio.dev, "Only VSC8584 revB is supported.\n"); - return -ENOTSUPP; - } - vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL); if (!vsc8531) return -ENOMEM; @@ -2587,9 +2576,8 @@ static struct phy_driver vsc85xx_driver[] = { .config_inband = vsc85xx_config_inband, }, { - .phy_id = PHY_ID_VSC856X, + PHY_ID_MATCH_EXACT(PHY_ID_VSC856X), .name = "Microsemi GE VSC856X SyncE", - .phy_id_mask = 0xfffffff0, /* PHY_GBIT_FEATURES */ .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, @@ -2667,9 +2655,8 @@ static struct phy_driver vsc85xx_driver[] = { .config_inband = vsc85xx_config_inband, }, { - .phy_id = PHY_ID_VSC8575, + PHY_ID_MATCH_EXACT(PHY_ID_VSC8575), .name = "Microsemi GE VSC8575 SyncE", - .phy_id_mask = 0xfffffff0, /* PHY_GBIT_FEATURES */ .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, @@ -2693,9 +2680,8 @@ static struct phy_driver vsc85xx_driver[] = { .config_inband = vsc85xx_config_inband, }, { - .phy_id = PHY_ID_VSC8582, + PHY_ID_MATCH_EXACT(PHY_ID_VSC8582), .name = "Microsemi GE VSC8582 SyncE", - .phy_id_mask = 0xfffffff0, /* PHY_GBIT_FEATURES */ .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, @@ -2719,9 +2705,8 @@ static struct phy_driver vsc85xx_driver[] = { .config_inband = vsc85xx_config_inband, }, { - .phy_id = PHY_ID_VSC8584, + PHY_ID_MATCH_EXACT(PHY_ID_VSC8584), .name = "Microsemi GE VSC8584 SyncE", - .phy_id_mask = 0xfffffff0, /* PHY_GBIT_FEATURES */ .soft_reset = &genphy_soft_reset, .config_init = &vsc8584_config_init, From e6bb3a49c5f9de870ea95e69775df785728e3366 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:48 +0200 Subject: [PATCH 482/697] iio: imu: st_lsm6dsx: fix stack leak in tagged FIFO buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c9d8e9adaa63150ef7e833480b799d0bab83a276 upstream. The tagged FIFO path declares iio_buff on the stack with __aligned(8) but no initializer, but there is a hole in the structure, which will then leak to userspace as ST_LSM6DSX_SAMPLE_SIZE bytes (6) will be copied, but the space between that and the timestamp are not initialized. Commit c14edb4d0bdc ("iio:imu:st_lsm6dsx Fix alignment and data leak issues") moved the untagged FIFO path to a kzalloc'd buffer in hw->scan, but for the tagged path it only added the alignment qualifier and not the initializer :( Fix this by just zero-initializing the structure on the stack. Cc: Lorenzo Bianconi Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: c14edb4d0bdc ("iio:imu:st_lsm6dsx Fix alignment and data leak issues") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 07b81e523e63..7cd12de50bf5 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -608,7 +608,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) * must be passed a buffer that is aligned to 8 bytes so * as to allow insertion of a naturally aligned timestamp. */ - u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8); + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE] __aligned(8) = { }; u8 tag; bool reset_ts = false; int i, err, read_len; From ce582b22dd2ff15ac99101c22ec1559d1febe2ff Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:49 +0200 Subject: [PATCH 483/697] iio: imu: adis16550: fix stack leak in trigger handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 474f8928d50b09f7dcf507049f08732640b88b49 upstream. adis16550_trigger_handler() declares the scan data array on the stack without initializing it. The memcpy() at the bottom fills only the first 28 bytes (TEMP + 6 channels of GYRO/ACCEL data), and iio_push_to_buffers_with_timestamp() writes the s64 timestamp at the 8-byte-aligned offset 32. Bytes 28-31 remain uninitialized stack data which leaks to userspace on ever trigger. Fix this all by just zero-initializing the structure on the stack. Cc: Lars-Peter Clausen Cc: Michael Hennerich Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: e4570f4bb231 ("iio: imu: adis16550: align buffers for timestamp") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/imu/adis16550.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/imu/adis16550.c b/drivers/iio/imu/adis16550.c index 1f2af506f4bd..75679612052f 100644 --- a/drivers/iio/imu/adis16550.c +++ b/drivers/iio/imu/adis16550.c @@ -836,7 +836,7 @@ static irqreturn_t adis16550_trigger_handler(int irq, void *p) u16 dummy; bool valid; struct iio_poll_func *pf = p; - __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8); + __be32 data[ADIS16550_MAX_SCAN_DATA] __aligned(8) = { }; struct iio_dev *indio_dev = pf->indio_dev; struct adis16550 *st = iio_priv(indio_dev); struct adis *adis = iio_device_get_drvdata(indio_dev); From a58400f58f82f3d8de9c067aa7cda690228c1ecc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 9 Apr 2026 15:40:47 +0200 Subject: [PATCH 484/697] iio: pressure: bmp280: fix stack leak in bmp580 trigger handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 387c86b582e0782ab332e7bfcd4e6e3f93922961 upstream. bmp580_trigger_handler() declares its scan buffer on the stack without an initializer and then memcpy()s 3 bytes of 24-bit sensor data into each 4-byte __le32 field. The high byte of comp_temp and comp_press is left uninitialized, and the channel storagebits is 32, so two bytes of stack are pushed to userspace per scan. This is a regression from when the buffer lived in the private data, the move to a stack-local struct dropped the implicit zeroing. bme280_trigger_handler() was fixed up to handle this bug, but this driver was not fixed because there was no padding hole, but rather a short-fill issue. Fix this all by just zero-initializing the structure on the stack. Cc: Jonathan Cameron Cc: David Lechner Cc: "Nuno Sá" Cc: Andy Shevchenko Fixes: 872c8014e05e ("iio: pressure: bmp280: drop sensor_data array") Cc: stable Assisted-by: gregkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman Reviewed-by: David Lechner Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/pressure/bmp280-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index d983ce9c0b99..9b489766e457 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -2616,7 +2616,7 @@ static irqreturn_t bmp580_trigger_handler(int irq, void *p) __le32 comp_temp; __le32 comp_press; aligned_s64 timestamp; - } buffer; + } buffer = { }; int ret; guard(mutex)(&data->lock); From a38ed87818b2419090fb1a6338ddce6842b65dfa Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 14 May 2026 19:10:06 +0200 Subject: [PATCH 485/697] usb: typec: ucsi: ccg: reject firmware images without a ':' record header commit d7486952bf74e546ee3748fb14b2d07881fa6273 upstream. do_flash() locates the first .cyacd record with p = strnchr(fw->data, fw->size, ':'); while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); ... } If the firmware image contains no ':' byte, strnchr() returns NULL. NULL compares less than the valid kernel pointer eof, so the loop body runs and strnchr() is called with p + 1 == (void *)1 and a length of roughly (unsigned long)eof, causing a wonderful crash. The not_signed_fw fallthrough earlier in do_flash() and the chip-state branches in ccg_fw_update_needed() allow an unsigned blob to reach this loop, so a root user who can place a crafted file under /lib/firmware and write the do_flash sysfs attribute can trigger the oops. Bail out with -EINVAL when the initial strnchr() returns NULL. Assisted-by: gkh_clanker_t1000 Cc: stable Cc: Heikki Krogerus Reviewed-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman Link: https://patch.msgid.link/2026051405-posture-shrill-7884@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_ccg.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index d83a0051c737..9d7b834a76fa 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1242,6 +1242,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode) *****************************************************************/ p = strnchr(fw->data, fw->size, ':'); + if (!p) { + dev_err(dev, "Bad FW format: no ':' record header found\n"); + err = -EINVAL; + goto release_mem; + } while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); From f9d787fbe83127105e42088cca40e5118db0d810 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:51 +0200 Subject: [PATCH 486/697] usb: typec: tcpm: validate VDO count in Discover Identity ACK handlers commit 8fbc349e8383125dd2d8de1c1e926279d398ab17 upstream. Properly validate the count passed from a device when calling svdm_consume_identity() or svdm_consume_identity_sop_prime() as the device-controlled value could index off of the static arrays, which could leak data. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: stable Reviewed-by: Badhri Jagan Sridharan Link: https://patch.msgid.link/2026051350-plated-salute-0efe@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 336a0de9a911..c92fe928ae43 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1695,6 +1695,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) u32 vdo = p[VDO_INDEX_IDH]; u32 product = p[VDO_INDEX_PRODUCT]; + if (cnt <= VDO_INDEX_PRODUCT) + return; + memset(&port->mode_data, 0, sizeof(port->mode_data)); port->partner_ident.id_header = vdo; @@ -1715,6 +1718,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p u32 product = p[VDO_INDEX_PRODUCT]; int svdm_version; + if (cnt <= VDO_INDEX_CABLE_1) + return; + /* * Attempt to consume identity only if cable currently is not set */ @@ -1738,7 +1744,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p switch (port->negotiated_rev_prime) { case PD_REV30: port->cable_desc.pd_revision = 0x0300; - if (port->cable_desc.active) + if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2) port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2]; break; case PD_REV20: From 4505f33dab56c274e82f47f94bf60a8cbf8f4b42 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:53 +0200 Subject: [PATCH 487/697] usb: typec: tcpm: bound altmode_desc[] per iteration in svdm_consume_modes() commit 3389c149c68c3fea61910ad5d34f7bf3bff44e32 upstream. svdm_consume_modes() checks pmdata->altmodes against the array size once before the loop over the count, but forgot to check the bound at every point in the loop. In the well-behaved SVDM discovery flow this is harmless because each of at most SVID_DISCOVERY_MAX SVIDs contributes at most MODE_DISCOVERY_MAX modes, exactly filling altmode_desc[ALTMODE_DISCOVERY_MAX]. But the CMDT_RSP_ACK handler in tcpm_pd_svdm() does not correlate an incoming ACK with any request the port actually sent. Once port->partner is set, an unsolicited Discover Modes ACK is consumed unconditionally. A broken or malicious port partner can therefore drive altmodes to ALTMODE_DISCOVERY_MAX - 1 via the normal flow, and then send one extra Discover Modes ACK with seven VDOs. Because the pre-loop check passes, the loop could then writes up to five entries past altmode_desc[]. For mode_data_prime the next field in struct tcpm_port is the partner_altmode[] pointer array, which then receives partner-chosen SVID/VDO bytes. Move the bound check inside the loop so the array can never be indexed past ALTMODE_DISCOVERY_MAX regardless of how many VDOs the partner supplies or how the function was reached. Assisted-by: gkh_clanker_t1000 Cc: Badhri Jagan Sridharan Cc: Heikki Krogerus Cc: stable Link: https://patch.msgid.link/2026051351-reshuffle-skillful-90af@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index c92fe928ae43..e722c9fc3c97 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1832,23 +1832,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, switch (rx_sop_type) { case TCPC_TX_SOP_PRIME: pmdata = &port->mode_data_prime; - if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; case TCPC_TX_SOP: pmdata = &port->mode_data; - if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; default: return; } for (i = 1; i < cnt; i++) { + if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { + /* Already logged in svdm_consume_svids() */ + return; + } paltmode = &pmdata->altmode_desc[pmdata->altmodes]; memset(paltmode, 0, sizeof(*paltmode)); From 052dbef45cb36b89b503d72ea6b22d5f5bb1e586 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:54 +0200 Subject: [PATCH 488/697] usb: typec: ucsi: displayport: NAK DP_CMD_CONFIGURE without a payload VDO commit 167dd8d12226587ee554f520aed0256b7769cd5d upstream. ucsi_displayport_vdm() handles a DP_CMD_CONFIGURE by copying the first payload VDO from data[], but unlike the equivalent handler in altmodes/displayport.c it does not check that count covers a VDO beyond the header. A header-only Configure VDM (count == 1) would read one u32 past the caller's array. In the normal UCSI path the caller controls count, so this is hardening for non-standard delivery paths. NAK and bail when no configuration VDO is present, matching the generic DP altmode driver's existing guard. Assisted-by: gkh_clanker_t1000 Cc: Pooja Katiyar Cc: Johan Hovold Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051351-vividly-flattered-eb3d@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/displayport.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..67a0991a7b76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, dp->header |= VDO_CMDT(CMDT_RSP_ACK); break; case DP_CMD_CONFIGURE: + if (count < 2) { + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + break; + } dp->data.conf = *data; if (ucsi_displayport_configure(dp)) { dp->header |= VDO_CMDT(CMDT_RSP_NAK); From b10eff5abe6aa2a5af10ed17bddff76e3b6e6b9b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:49 +0200 Subject: [PATCH 489/697] usb: typec: altmodes/displayport: validate count before reading Status Update VDO commit 8a18f896e667df491331371b55d4ad644dc51d60 upstream. A broken/malicious device can send the incorrect count for a status update VDO, which will cause the kernel to read uninitialized stack data and send it off elsewhere. Fix this up by correctly verifying the count for the update object. Assisted-by: gkh_clanker_t1000 Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051350-reacquire-sculpture-4244@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 3edc9dc86b25..9d1d87a6ae77 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -405,6 +405,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt, dp->state = DP_STATE_EXIT_PRIME; break; case DP_CMD_STATUS_UPDATE: + if (count < 2) + break; dp->data.status = *vdo; ret = dp_altmode_status_update(dp); break; From e94933dc41b87503bf585c8c6d53d740620eceb9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:48 +0200 Subject: [PATCH 490/697] usb: typec: wcove: don't write past struct pd_message in wcove_read_rx_buffer() commit 4af7ad0e6d7aa4403dbb1dac7b9659b0421efcaa upstream. wcove_read_rx_buffer() copies the PD RX FIFO into the caller's struct pd_message with for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); which has two problems: USBC_RXINFO_RXBYTES() is a 5-bit field (max 31) while struct pd_message is 30 bytes (__le16 header + __le32 payload[PD_MAX_PAYLOAD], packed). The byte count latched in RXINFO is the number of bytes the port partner put on the wire, so a malicious partner that transmits a 31-byte frame can drive the loop one byte past the destination if the WCOVE BMC receiver does not enforce the PD object-count limit in hardware. The existing FIXME flagged this as unverified. Independently, regmap_read() takes an unsigned int * and stores a full unsigned int at the destination. Passing the byte pointer msg + i means each iteration writes four bytes; the high three are zero (val_bits is 8) and are normally overwritten by the next iteration, but the final iteration's high bytes are not. With RXBYTES == 30 the i == 29 iteration already writes three zero bytes past msg, which sits on the IRQ thread's stack in wcove_typec_irq(). Clamp the loop to sizeof(struct pd_message) and read each register into a local before storing only its low byte, so the copy can never exceed the destination regardless of what RXINFO reports. Assisted-by: gkh_clanker_t1000 Cc: stable Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/2026051347-clustered-deflected-9543@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/wcove.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 759c982bb16a..0e5a3e277c3e 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl); } -static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) +static int wcove_read_rx_buffer(struct wcove_typec *wcove, + struct pd_message *msg) { - unsigned int info; + unsigned int info, val, len; + u8 *buf = (u8 *)msg; int ret; int i; @@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) if (ret) return ret; - /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */ + len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg)); - for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) { - ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); + for (i = 0; i < len; i++) { + ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val); if (ret) return ret; + buf[i] = val; } return regmap_write(wcove->regmap, USBC_RXSTATUS, From 9b496e3371c04f0a03b7faa5d2442536d00e3998 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:50 +0200 Subject: [PATCH 491/697] usb: typec: tcpm/tcpci_maxim: validate header NDO against RX_BYTE_CNT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit aa2f716327be1818e1cb156da8a2844804aaec2f upstream. A broken/malicious port can transmit a CRC-valid frame whose header advertises up to seven data objects but whose body carries fewer than that. Check for this, and rightfully reject the message, instead of reading from uninitialized stack memory. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: "André Draszik" Cc: Badhri Jagan Sridharan Cc: Amit Sunil Dhamne Cc: stable Link: https://patch.msgid.link/2026051350-sitter-canopener-9045@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index 19f638650796..cdf6489e1924 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -186,6 +186,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET; msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr); rx_buf_ptr = rx_buf_ptr + sizeof(msg.header); + + if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) + + pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) { + max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n", + count, pd_header_cnt_le(msg.header)); + return; + } + for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++, rx_buf_ptr += sizeof(msg.payload[0])) msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr); From 0edd1e21587b0483c7ceb993b9fb9668bbef7433 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 13 May 2026 17:52:55 +0200 Subject: [PATCH 492/697] usb: typec: ucsi: validate connector number in ucsi_connector_change() commit 288a81a8507052bcfbf884d39a463c44c42c5fd9 upstream. The connector number in a UCSI CCI notification is a 7-bit field supplied by the PPM. ucsi_connector_change() uses it to index the ucsi->connector[] array without checking it against the number of connectors the PPM reported at init time, so a buggy or malicious PPM (EC firmware, or an I2C-attached UCSI controller on the ccg / stm32g0 / glink transports) can drive schedule_work() on memory past the end of the array. Reject connector numbers that are zero or exceed cap.num_connectors before dereferencing the array. Assisted-by: gkh_clanker_t1000 Cc: Heikki Krogerus Cc: Benson Leung Cc: Jameson Thies Cc: Nathan Rebello Cc: Johan Hovold Cc: Pooja Katiyar Cc: Hsin-Te Yuan Cc: Abel Vesa Cc: stable Reviewed-by: Abel Vesa Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/2026051351-truck-steadfast-df48@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 47f9a8046109..175c785347e7 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1325,13 +1325,22 @@ static void ucsi_handle_connector_change(struct work_struct *work) */ void ucsi_connector_change(struct ucsi *ucsi, u8 num) { - struct ucsi_connector *con = &ucsi->connector[num - 1]; + struct ucsi_connector *con; if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { dev_dbg(ucsi->dev, "Early connector change event\n"); return; } + if (!num || num > ucsi->cap.num_connectors) { + dev_warn_ratelimited(ucsi->dev, + "Bogus connector change on %u (max %u)\n", + num, ucsi->cap.num_connectors); + return; + } + + con = &ucsi->connector[num - 1]; + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) schedule_work(&con->work); } From f793b67d41e5fab719c5a90baa77cbd2fe259517 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:22:18 +0200 Subject: [PATCH 493/697] USB: serial: safe_serial: fix memory corruption with small endpoint commit 438061ed1ad85e6743e2dce826671772d81089ec upstream. Make sure that the bulk-out buffer size is at least eight bytes to avoid user-controlled slab corruption in "safe" mode should a malicious device report a smaller size. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/safe_serial.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 238b54993446..d267a31dcccf 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port, static int safe_startup(struct usb_serial *serial) { struct usb_interface_descriptor *desc; + int bulk_out_size; if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS) return -ENODEV; @@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial) default: return -EINVAL; } + + /* + * The bulk-out buffer needs to be large enough for the two-byte + * trailer in safe mode, but assume anything smaller than eight bytes + * is broken. + */ + bulk_out_size = serial->port[0]->bulk_out_size; + if (bulk_out_size > 0 && bulk_out_size < 8) + return -EINVAL; + return 0; } From f33b5a61673bd220fdaaf4202cf1013d6d66c943 Mon Sep 17 00:00:00 2001 From: Henri A Date: Wed, 20 May 2026 10:25:44 -0400 Subject: [PATCH 494/697] media: rc: igorplugusb: fix control request setup packet commit 171022c7d594c133a45f92357a2a91475edabe20 upstream. Commit eac69475b01f ("media: rc: igorplugusb: heed coherency rules") changed the control request storage from an embedded struct to an allocated pointer so it can obey DMA coherency rules. However, the driver still passes &ir->request to usb_fill_control_urb(). That points the URB setup packet at the pointer field itself rather than at the allocated struct usb_ctrlrequest. USB core then interprets pointer bytes as the setup packet. This can produce an invalid bRequestType and trigger the control direction warning reported by syzbot: usb 2-1: BOGUS control dir, pipe 80003580 doesn't match bRequestType 0 Pass ir->request itself as the setup packet. Fixes: eac69475b01f ("media: rc: igorplugusb: heed coherency rules") Reported-by: syzbot+11f0e4f957c7c3bf3d51@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=11f0e4f957c7c3bf3d51 Tested-by: syzbot+11f0e4f957c7c3bf3d51@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5.5 Signed-off-by: Henri A Signed-off-by: Sean Young Signed-off-by: Hans Verkuil Signed-off-by: Greg Kroah-Hartman --- drivers/media/rc/igorplugusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/rc/igorplugusb.c b/drivers/media/rc/igorplugusb.c index 439f95e6879f..48931b833b43 100644 --- a/drivers/media/rc/igorplugusb.c +++ b/drivers/media/rc/igorplugusb.c @@ -184,7 +184,7 @@ static int igorplugusb_probe(struct usb_interface *intf, if (!ir->buf_in) goto fail; usb_fill_control_urb(ir->urb, udev, - usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, + usb_rcvctrlpipe(udev, 0), (uint8_t *)ir->request, ir->buf_in, MAX_PACKET, igorplugusb_callback, ir); usb_make_path(udev, ir->phys, sizeof(ir->phys)); From 8735a28f2dcd6facc199752162592eb98ae4b419 Mon Sep 17 00:00:00 2001 From: Thomas Fourier Date: Fri, 22 May 2026 10:54:04 +0200 Subject: [PATCH 495/697] Input: ims-pcu - fix usb_free_coherent() size in ims_pcu_buffers_free() commit dab48a7e74e6a394f3aa0461a2b1fb0c7b38fcb8 upstream. The input buffer size is pcu->max_in_size, but pcu->max_out_size is passed to usb_free_coherent(). Change size to match the allocation size. Fixes: 628329d52474 ("Input: add IMS Passenger Control Unit driver") Cc: stable@vger.kernel.org Signed-off-by: Thomas Fourier Link: https://patch.msgid.link/20260522085412.45430-2-fourier.thomas@gmail.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/ims-pcu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 4581f1c53644..22d34644254d 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1604,7 +1604,7 @@ static void ims_pcu_buffers_free(struct ims_pcu *pcu) usb_kill_urb(pcu->urb_in); usb_free_urb(pcu->urb_in); - usb_free_coherent(pcu->udev, pcu->max_out_size, + usb_free_coherent(pcu->udev, pcu->max_in_size, pcu->urb_in_buf, pcu->read_dma); kfree(pcu->urb_out_buf); From 6728e80c9d292b194133c9b98663ce14784884fd Mon Sep 17 00:00:00 2001 From: hlleng Date: Tue, 12 May 2026 09:57:37 +0800 Subject: [PATCH 496/697] HID: quirks: Add ALWAYS_POLL quirk for SIGMACHIP USB mouse commit 07466fc91c55532edcfb5c6a7ccd2ea52728d6bd upstream. The SIGMACHIP USB mouse with VID/PID 1c4f:0034 can disconnect and re-enumerate repeatedly after it has been enumerated if its interrupt endpoint is not continuously polled. This was observed with the device reporting itself as "SIGMACHIP Usb Mouse". Keeping the input event device open avoids the disconnects. Add HID_QUIRK_ALWAYS_POLL for this device so the HID core keeps polling it even when there is no userspace input consumer. Cc: stable@vger.kernel.org Signed-off-by: hlleng Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-quirks.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index a24592893345..96a9fa53e4ad 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1244,6 +1244,7 @@ #define USB_VENDOR_ID_SIGMA_MICRO 0x1c4f #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD 0x0002 +#define USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE 0x0034 #define USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD2 0x0059 #define USB_VENDOR_ID_SIGMATEL 0x066F diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 04d3ec360c1d..ebf865c2dbb3 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -186,6 +186,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD), HID_QUIRK_NO_INIT_REPORTS }, + { HID_USB_DEVICE(USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_USB_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH), HID_QUIRK_NOGET }, From 4064a30381fadefc42114fcbd178dd1c73626e88 Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Thu, 21 May 2026 13:25:47 +0800 Subject: [PATCH 497/697] Bluetooth: btusb: Allow firmware re-download when version matches commit 82855073c1081732656734b74d7d1d5e4cfd0da7 upstream. The Bluetooth host decides whether to download firmware by reading the controller firmware download completion flag and firmware version information. If a USB error occurs during the firmware download process (for example due to a USB disconnect), the download is aborted immediately. An incomplete firmware transfer does not cause the controller to set the download completion flag, but the firmware version information may be updated at an early stage of the download process. In this case, after USB reconnection, the host attempts to re-download the firmware because the download completion flag is not set. However, since the controller reports the same firmware version as the target firmware, the download is skipped. This ultimately results in the firmware not being properly updated on the controller. This change removes the restriction that skips firmware download when the versions are equal. It covers scenarios where the USB connection can be disconnected at any time and ensures that firmware download can be retriggered after USB reconnection, allowing the Bluetooth firmware to be correctly and completely updated. Fixes: 3267c884cefa ("Bluetooth: btusb: Add support for QCA ROME chipset family") Cc: stable@vger.kernel.org Signed-off-by: Shuai Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/btusb.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 40f0c3b4eee6..25236fd97aed 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3478,7 +3478,13 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev, "firmware rome 0x%x build 0x%x", rver_rom, rver_patch, ver_rom, ver_patch); - if (rver_rom != ver_rom || rver_patch <= ver_patch) { + /* Allow rampatch when the patch version equals the firmware version. + * A firmware download may be aborted by a transient USB error (e.g. + * disconnect) after the controller updates version info but before + * completion. + * Allowing equal versions enables re-flashing during recovery. + */ + if (rver_rom != ver_rom || rver_patch < ver_patch) { bt_dev_err(hdev, "rampatch file version did not match with firmware"); err = -EINVAL; goto done; From 0886c6f257fe3663f80218aa1919b0f3f21bf22c Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 25 May 2026 14:48:58 +0200 Subject: [PATCH 498/697] hpfs: fix a crash if hpfs_map_dnode_bitmap fails commit 974820a59efde7c1a7e1260bcfe9bb81f833cc9f upstream. If hpfs_map_dnode_bitmap fails, the code would call hpfs_brelse4 on uninitialized quad buffer head, causing a crash. Signed-off-by: Mikulas Patocka Reported-by: Farhad Alemi Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- fs/hpfs/alloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c index 66617b1557c6..f5150372618e 100644 --- a/fs/hpfs/alloc.c +++ b/fs/hpfs/alloc.c @@ -372,8 +372,8 @@ int hpfs_check_free_dnodes(struct super_block *s, int n) return 0; } } + hpfs_brelse4(&qbh); } - hpfs_brelse4(&qbh); i = 0; if (hpfs_sb(s)->sb_c_bitmap != -1) { bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1"); From 0ba6c05156d9ff9fc6ca22b7690e2eec9eca66f7 Mon Sep 17 00:00:00 2001 From: SeongJae Park Date: Mon, 18 May 2026 08:25:58 -0700 Subject: [PATCH 499/697] mm/damon/sysfs-schemes: delete tried region in regions_rmdirs() commit 441f92f7d386b85bad16de49db95a307cba048a2 upstream. DAMON sysfs maintains the DAMOS tried region directory objects via a linked list. When the user requests refresh of the directories, DAMON sysfs removes all the region directories first, and then generate updated regions directory on the empty space. The removal function (damon_sysfs_scheme_regions_rm_dirs()) only puts the kobj objects. Deletion of the container region object from the linked list is done inside the kobj release callback function. If somehow the callback invocation is delayed, the list will contain regions list that gonna be freed. If the updated region directories creation is started in this situation, the list can be corrupted and use-after-free can happen. Because the kobj objects are managed by only DAMON sysfs, the issue cannot happen in normal situation. But, such delays can be made on kernels that built with CONFIG_DEBUG_KOBJECT_RELEASE. On the kernel, the issue can indeed be reproduced like below. # damo start --damos_action stat # cd /sys/kernel/mm/damon/admin/kdamonds/0/ # for i in {1..10}; do echo update_schemes_tried_regions > state; done # dmesg | grep underflow [ 89.296152] refcount_t: underflow; use-after-free. Fix the issue by removing the region object from the list when decrementing the reference count. Also update damos_sysfs_populate_region_dir() to add the region object to the list only after the kobject_init_and_add() is success, so that fail of kobject_init_and_add() is not leaving the deallocated object on the list. The issue was discovered [1] by Sashiko. Link: https://lore.kernel.org/20260518152559.93038-1-sj@kernel.org Link: https://lore.kernel.org/20260513011920.119183-1-sj@kernel.org [1] Fixes: 9277d0367ba1 ("mm/damon/sysfs-schemes: implement scheme region directory") Signed-off-by: SeongJae Park Cc: # 6.2.x Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/damon/sysfs-schemes.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mm/damon/sysfs-schemes.c b/mm/damon/sysfs-schemes.c index a7be6ea812e4..4a432a07c5c3 100644 --- a/mm/damon/sysfs-schemes.c +++ b/mm/damon/sysfs-schemes.c @@ -89,7 +89,6 @@ static void damon_sysfs_scheme_region_release(struct kobject *kobj) struct damon_sysfs_scheme_region *region = container_of(kobj, struct damon_sysfs_scheme_region, kobj); - list_del(®ion->list); kfree(region); } @@ -166,7 +165,7 @@ static void damon_sysfs_scheme_regions_rm_dirs( struct damon_sysfs_scheme_region *r, *next; list_for_each_entry_safe(r, next, ®ions->regions_list, list) { - /* release function deletes it from the list */ + list_del(&r->list); kobject_put(&r->kobj); regions->nr_regions--; } @@ -2767,14 +2766,15 @@ void damos_sysfs_populate_region_dir(struct damon_sysfs_schemes *sysfs_schemes, if (!region) return; region->sz_filter_passed = sz_filter_passed; - list_add_tail(®ion->list, &sysfs_regions->regions_list); - sysfs_regions->nr_regions++; if (kobject_init_and_add(®ion->kobj, &damon_sysfs_scheme_region_ktype, &sysfs_regions->kobj, "%d", sysfs_regions->nr_regions++)) { kobject_put(®ion->kobj); + return; } + list_add_tail(®ion->list, &sysfs_regions->regions_list); + sysfs_regions->nr_regions++; } int damon_sysfs_schemes_clear_regions( From a3cc795129e5ec0f8948653a3bf471e7d8852f5e Mon Sep 17 00:00:00 2001 From: Linpu Yu Date: Sun, 10 May 2026 13:43:30 +0800 Subject: [PATCH 500/697] ipc: limit next_id allocation to the valid ID range commit fa0b9b2b7ae3539908d69c2b9ac0d144d9bc5139 upstream. The checkpoint/restore sysctl path can request the next SysV IPC id through ids->next_id. ipc_idr_alloc() currently forwards that request to idr_alloc() with an open-ended upper bound. If the valid tail of the SysV IPC id space is full, the allocation can spill beyond ipc_mni. The returned SysV IPC id still uses the normal index encoding, so later lookup and removal can target the wrong slot. This leaves the real IDR entry behind and breaks the IDR state for the object. The bug is in ipc_idr_alloc() in the checkpoint/restore path. 1. ids->next_id is passed to: idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id), 0, ...) 2. The zero upper bound makes the allocation effectively open-ended. Once the valid SysV IPC tail is occupied, idr_alloc() can spill past ipc_mni and allocate an entry beyond the valid IPC id range. 3. The new object id is still encoded with the narrower SysV IPC index width: new->id = (new->seq << ipcmni_seq_shift()) + idx 4. Later removal goes through ipc_rmid(), which uses: ipcid_to_idx(ipcp->id) That truncates the real IDR index. An object actually stored at a high index can then be removed as if it lived at a low in-range index. 5. For shared memory, shm_destroy() frees the current object anyway, but the real high IDR slot is left behind as a dangling pointer. 6. A subsequent walk of /proc/sysvipc/shm reaches the stale IDR entry and dereferences freed memory. Prevent this by bounding the requested allocation to ipc_mni so the checkpoint/restore path fails once the valid range is exhausted. Link: https://lore.kernel.org/cover.1778336914.git.linpu5433@gmail.com Link: https://lore.kernel.org/2eebe949bfa7d1f6e13b5be6a92c64c850ce9d45.1778336914.git.linpu5433@gmail.com Fixes: 03f595668017 ("ipc: add sysctl to specify desired next object id") Signed-off-by: Linpu Yu Signed-off-by: Ren Wei Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Cc: Kees Cook Cc: Stanislav Kinsbursky Cc: Davidlohr Bueso Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- ipc/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/util.c b/ipc/util.c index cae60f11d9c2..df1f0c291cdc 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -253,7 +253,7 @@ static inline int ipc_idr_alloc(struct ipc_ids *ids, struct kern_ipc_perm *new) } else { new->seq = ipcid_to_seqx(next_id); idx = idr_alloc(&ids->ipcs_idr, new, ipcid_to_idx(next_id), - 0, GFP_NOWAIT); + ipc_mni, GFP_NOWAIT); } if (idx >= 0) new->id = (new->seq << ipcmni_seq_shift()) + idx; From f1f0cdca932be50f278385574661822abb9ae326 Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Mon, 18 May 2026 10:28:19 +0200 Subject: [PATCH 501/697] mm: memcontrol: propagate NMI slab stats to memcg vmstats commit e16f17a9c5af50221184d1ef4be4056bf3c4209e upstream. flush_nmi_stats() drains per-node NMI slab atomics into the per-node lruvec_stats, but does not propagate them to the memcg-level vmstats. For non NMI case, account_slab_nmi_safe() calls mod_memcg_lruvec_state() which updates both per-node lruvec_stats and memcg-level vmstats, so flush_nmi_stats() needs to flush to per-node lruvec_stats as well as memcg-level vmstats. So fix this by flushing to the memcg-level vmstats for NMI too. Link: https://lore.kernel.org/20260518082830.599102-1-alex@ghiti.fr Fixes: 940b01fc8dc1 ("memcg: nmi safe memcg stats for specific archs") Signed-off-by: Alexandre Ghiti Acked-by: Shakeel Butt Acked-by: Johannes Weiner Reviewed-by: Harry Yoo (Oracle) Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memcontrol.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 4df68e5468ad..80e71a17d500 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4044,6 +4044,9 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent, lstats->state[index] += slab; if (plstats) plstats->state_pending[index] += slab; + memcg->vmstats->state[index] += slab; + if (parent) + parent->vmstats->state_pending[index] += slab; } if (atomic_read(&pn->slab_unreclaimable)) { int slab = atomic_xchg(&pn->slab_unreclaimable, 0); @@ -4052,6 +4055,9 @@ static void flush_nmi_stats(struct mem_cgroup *memcg, struct mem_cgroup *parent, lstats->state[index] += slab; if (plstats) plstats->state_pending[index] += slab; + memcg->vmstats->state[index] += slab; + if (parent) + parent->vmstats->state_pending[index] += slab; } } } From 0995d1f79aed8ccbf62056189dd53fd19726ea08 Mon Sep 17 00:00:00 2001 From: "Pratyush Yadav (Google)" Date: Tue, 5 May 2026 15:39:20 +0200 Subject: [PATCH 502/697] memfd: deny writeable mappings when implying SEAL_WRITE commit 3b041514cb6eae45869b020f743c14d983363222 upstream. When SEAL_EXEC is added, SEAL_WRITE is implied to make W^X. But the implied seal is set after the check that makes sure the memfd can not have any writable mappings. This means one can use SEAL_EXEC to apply SEAL_WRITE while having writeable mappings. This breaks the contract that SEAL_WRITE provides and can be used by an attacker to pass a memfd that appears to be write sealed but can still be modified arbitrarily. Fix this by adding the implied seals before the call for mapping_deny_writable() is done. Link: https://lore.kernel.org/20260505133922.797635-1-pratyush@kernel.org Fixes: c4f75bc8bd6b ("mm/memfd: add write seals when apply SEAL_EXEC to executable memfd") Signed-off-by: Pratyush Yadav (Google) Reviewed-by: Pasha Tatashin Acked-by: Jeff Xu Cc: Baolin Wang Cc: Brendan Jackman Cc: Greg Thelen Cc: Hugh Dickins Cc: Kees Cook Cc: "David Hildenbrand (Arm)" Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/memfd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mm/memfd.c b/mm/memfd.c index a405eaa451ee..f7ba1e9dea49 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -284,6 +284,12 @@ static int memfd_add_seals(struct file *file, unsigned int seals) goto unlock; } + /* + * SEAL_EXEC implies SEAL_WRITE, making W^X from the start. + */ + if (seals & F_SEAL_EXEC && inode->i_mode & 0111) + seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE; + if ((seals & F_SEAL_WRITE) && !(*file_seals & F_SEAL_WRITE)) { error = mapping_deny_writable(file->f_mapping); if (error) @@ -296,12 +302,6 @@ static int memfd_add_seals(struct file *file, unsigned int seals) } } - /* - * SEAL_EXEC implies SEAL_WRITE, making W^X from the start. - */ - if (seals & F_SEAL_EXEC && inode->i_mode & 0111) - seals |= F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_FUTURE_WRITE; - *file_seals |= seals; error = 0; From 0fcc34d0d8fefca4fea349e45c10e3a3d90350eb Mon Sep 17 00:00:00 2001 From: Dev Jain Date: Mon, 18 May 2026 12:06:56 +0530 Subject: [PATCH 503/697] mm/rmap: initialize nr_pages to 1 at loop start in try_to_unmap_one commit 3f8968e9cbf95d5d87d32218906cab0b9b9eddbe upstream. Initialize nr_pages to 1 at the start of each loop iteration, like folio_referenced_one() does. Without this, nr_pages computed by a previous folio_unmap_pte_batch() call can be reused on a later iteration that does not run folio_unmap_pte_batch() again. mmap a 64K large folio with MAP_ANONYMOUS | MAP_DROPPABLE, then call madvise(MADV_FREE), then make the last page device-exclusive via HMM_DMIRROR_EXCLUSIVE. Trigger node reclaim through sysfs. Now, in try_to_unmap_one(), we will first clear the first 15 out of 16 entries mapping the lazyfree folio. This will set nr_pages to 15. In the next pvmw walk, this nr_pages gets reused on a device-exclusive pte, thus potentially corrupting folio refcount/mapcount. At the moment, I have a userspace program which can make the kernel spit out a trace, but the blow up is in folio_referenced_one(), because there are existing bugs in the interaction between device-private and rmap (which too I am investigating). I did a one liner kernel change to avoid going into folio_referenced_one(), and the kernel blows up at folio_remove_rmap_ptes in try_to_unmap_one which is what I wanted. Note that the bug is there not since file folio batching but lazyfree folio batching, since device-exclusive only works for anonymous folios. Userspace visible effect is simply kernel crashing somewhere due to refcount/mapcount corruption. Link: https://lore.kernel.org/20260518063656.3721056-1-dev.jain@arm.com Fixes: 354dffd29575 ("mm: support batched unmap for lazyfree large folios during reclamation") Signed-off-by: Dev Jain Acked-by: Barry Song Acked-by: David Hildenbrand (Arm) Reviewed-by: Lorenzo Stoakes Cc: Anshuman Khandual Cc: Barry Song Cc: Dev Jain Cc: Harry Yoo Cc: Jann Horn Cc: Liam R. Howlett Cc: Rik van Riel Cc: Ryan Roberts Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- mm/rmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/rmap.c b/mm/rmap.c index 8f3c50bd1125..4c78c7109909 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -1918,6 +1918,8 @@ static bool try_to_unmap_one(struct folio *folio, struct vm_area_struct *vma, mmu_notifier_invalidate_range_start(&range); while (page_vma_mapped_walk(&pvmw)) { + nr_pages = 1; + /* * If the folio is in an mlock()d vma, we must not swap it out. */ From 197476b126010bac1b3199833c6966cd6f54c2a9 Mon Sep 17 00:00:00 2001 From: Stepan Ionichev Date: Thu, 14 May 2026 22:43:42 +0500 Subject: [PATCH 504/697] auxdisplay: line-display: fix OOB read on zero-length message_store() commit a7511dcd9dd4bc55d123f9b800c8a4ed2662e5c6 upstream. linedisp_display() unconditionally reads msg[count - 1] before checking whether count is zero, so a write of zero bytes to the message sysfs attribute hits msg[-1]: write(fd, "", 0); -> message_store(..., buf, count=0) -> linedisp_display(linedisp, buf, count=0) -> msg[count - 1] == '\n' ; OOB read The kernfs write buffer for that store is a 1-byte allocation (kernfs_fop_write_iter() does kmalloc(len + 1) with len == 0), so msg[-1] is a 1-byte read before the slab object. On a KASAN-enabled kernel this trips an out-of-bounds report and panics; on stock kernels it silently reads adjacent slab data and, if that byte happens to be '\n', the following count-- wraps ssize_t 0 to -1 and is then passed to kmemdup_nul(). linedisp_display() is reached from the message_store() sysfs callback (drivers/auxdisplay/line-display.c message attribute, mode 0644) and from the in-tree initial-message setup with count == -1, so the OOB path is only userspace-triggerable via zero-byte writes; vfs_write() does not short-circuit on count == 0 and kernfs_fop_write_iter() dispatches the store callback regardless. Guard the trailing-newline trim with a count check. The existing if (!count) block then takes the clear-display path unchanged. Affects every auxdisplay driver that registers via linedisp_register() / linedisp_attach(): ht16k33, max6959, img-ascii-lcd, seg-led-gpio. Fixes: 7e76aece6f03 ("auxdisplay: Extract character line display core support") Signed-off-by: Stepan Ionichev Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/auxdisplay/line-display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c index 8590a4cd21e0..35dc24013e9d 100644 --- a/drivers/auxdisplay/line-display.c +++ b/drivers/auxdisplay/line-display.c @@ -90,7 +90,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg, count = strlen(msg); /* if the string ends with a newline, trim it */ - if (msg[count - 1] == '\n') + if (count && msg[count - 1] == '\n') count--; if (!count) { From 388051f7389a61697c69691e5a40bb977cebd57c Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 22 May 2026 18:28:49 -0500 Subject: [PATCH 505/697] smb: client: fix uninitialized variable in smb2_writev_callback commit 9d2491197a00acf8c423512078458c2855102b66 upstream. compiling with W=2 pointed out that "written may be used uninitialized" Fixes: 20d72b00ca81 ("netfs: Fix the request's work item to not require a ref") Cc: stable@vger.kernel.org Reviewed-by: David Howells Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/client/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3e49c5b396b5..fa77dd31a39c 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -4837,7 +4837,7 @@ smb2_writev_callback(struct mid_q_entry *mid) unsigned int rreq_debug_id = wdata->rreq->debug_id; unsigned int subreq_debug_index = wdata->subreq.debug_index; ssize_t result = 0; - size_t written; + size_t written = 0; WARN_ONCE(wdata->server != mid->server, "wdata server %p != mid server %p", From 859d3ace791ed878ae9ba5522c7844d960da8f88 Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Wed, 20 May 2026 22:12:20 -0400 Subject: [PATCH 506/697] Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen() commit 8c8e620467a7b51562dbcefbd1f09f288d7d710d upstream. l2cap_chan_close() removes the channel from conn->chan_l, which must be done under conn->lock. cleanup_listen() runs under the parent sk_lock, so acquiring conn->lock would invert the established conn->lock -> chan->lock -> sk_lock order. Instead of calling l2cap_chan_close() directly, schedule l2cap_chan_timeout with delay 0 to close the channel asynchronously. The timeout handler already acquires conn->lock and chan->lock in the correct order. The timer is only armed when chan->conn is still set: if it is already NULL, l2cap_conn_del() has already processed this channel (l2cap_chan_del + l2cap_sock_teardown_cb + l2cap_sock_close_cb), so there is nothing left to do. If l2cap_conn_del() races in after the timer is armed, __clear_chan_timer() inside l2cap_chan_del() cancels it; if the timer has already fired, the handler returns harmlessly because chan->conn was cleared. Fixes: 3df91ea20e74 ("Bluetooth: Revert to mutexes from RCU list") Cc: # 0b58004: Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del() Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_sock.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 898ee21d7e4f..506c8c0b4d75 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1481,6 +1481,10 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) * pin it (hold_unless_zero() additionally skips a chan already past * its last reference). We then drop the sk lock before taking * chan->lock, so sk and chan locks are never held together. + * + * Since we cannot call l2cap_chan_close() without conn->lock, + * schedule l2cap_chan_timeout to close the channel; it already + * acquires conn->lock -> chan->lock in the correct order. */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan; @@ -1498,14 +1502,12 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) state_to_string(chan->state)); l2cap_chan_lock(chan); - __clear_chan_timer(chan); - l2cap_chan_close(chan, ECONNRESET); - /* l2cap_conn_del() may already have killed this socket - * (it sets SOCK_DEAD); skip the duplicate to avoid a - * double sock_put()/l2cap_chan_put(). + /* Since we cannot call l2cap_chan_close() without + * conn->lock, schedule its timer to trigger the close + * and cleanup of this channel. */ - if (!sock_flag(sk, SOCK_DEAD)) - l2cap_sock_kill(sk); + if (chan->conn) + __set_chan_timer(chan, 0); l2cap_chan_unlock(chan); l2cap_chan_put(chan); From e8a5baff5be273ca07771fd2b9bb1f2a4152917b Mon Sep 17 00:00:00 2001 From: Siwei Zhang Date: Wed, 20 May 2026 22:30:36 -0400 Subject: [PATCH 507/697] Bluetooth: L2CAP: fix chan ref leak in l2cap_chan_timeout() on !conn commit 9dbd84990394c51f5cee1e8871bb5ff8af5ed939 upstream. __set_chan_timer() takes a l2cap_chan reference via l2cap_chan_hold() before scheduling the delayed work. The normal path in l2cap_chan_timeout() drops this reference with l2cap_chan_put() at the end, but the early return when chan->conn is NULL skips the put, leaking the reference. Add the missing l2cap_chan_put() before the early return. Fixes: adf0398cee86 ("Bluetooth: l2cap: fix null-ptr-deref in l2cap_chan_timeout") Cc: stable@vger.kernel.org Signed-off-by: Siwei Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/l2cap_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9805908e6c6a..c7247360f9f9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -411,8 +411,10 @@ static void l2cap_chan_timeout(struct work_struct *work) BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - if (!conn) + if (!conn) { + l2cap_chan_put(chan); return; + } mutex_lock(&conn->lock); /* __set_chan_timer() calls l2cap_chan_hold(chan) while scheduling From 6348dfed5b0f9c6074f14322332e97493d32fef0 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Wed, 20 May 2026 18:56:43 -0400 Subject: [PATCH 508/697] Bluetooth: HIDP: fix missing length checks in hidp_input_report() commit 2a3ac9ee11dbb9845f3947cef4a79dba658cf6f6 upstream. hidp_input_report() reads keyboard and mouse payload data from an skb without first verifying that skb->len contains enough data. hidp_recv_intr_frame() pulls the 1-byte HIDP header before dispatching to hidp_input_report(). If a paired device sends a truncated packet, the handler reads beyond the valid skb data, resulting in an out-of-bounds read of skb data. The OOB bytes may be interpreted as phantom key presses or spurious mouse movement. Replace the open-coded length tracking and pointer arithmetic with skb_pull_data() calls. skb_pull_data() returns NULL if the requested bytes are not present, eliminating the need for a manual size variable and the separate skb->len guard. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hidp/core.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index e0e400381550..90d32ea8f979 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -179,12 +179,21 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) { struct input_dev *dev = session->input; unsigned char *keys = session->keys; - unsigned char *udata = skb->data + 1; - signed char *sdata = skb->data + 1; - int i, size = skb->len - 1; + unsigned char *udata; + signed char *sdata; + u8 *hdr; + int i; + + hdr = skb_pull_data(skb, 1); + if (!hdr) + return; - switch (skb->data[0]) { + switch (*hdr) { case 0x01: /* Keyboard report */ + udata = skb_pull_data(skb, 8); + if (!udata) + break; + for (i = 0; i < 8; i++) input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); @@ -213,6 +222,10 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) break; case 0x02: /* Mouse report */ + sdata = skb_pull_data(skb, 3); + if (!sdata) + break; + input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); @@ -222,7 +235,7 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) input_report_rel(dev, REL_X, sdata[1]); input_report_rel(dev, REL_Y, sdata[2]); - if (size > 3) + if (skb->len > 0) input_report_rel(dev, REL_WHEEL, sdata[3]); break; } From c318aa51830a3d2cc1229968fe521441c97356cd Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Wed, 27 May 2026 04:59:17 +0000 Subject: [PATCH 509/697] Bluetooth: ISO: fix UAF in iso_recv_frame commit 47f23a259517abbdb8032c057a1e8a6bf3734878 upstream. iso_recv_frame reads conn->sk under iso_conn_lock but releases the lock before using sk, with no reference held. A concurrent iso_sock_kill() can free sk in that window, causing use-after-free on sk->sk_state and sock_queue_rcv_skb(). Fix by replacing the bare pointer read with iso_sock_hold(conn), which calls sock_hold() while the spinlock is held, atomically elevating the refcount before the lock drops. Add a drop_put label so sock_put() is called on all exit paths where the hold succeeded. Fixes: ccf74f2390d60a2f9a75ef496d2564abb478f46a ("Bluetooth: Add BTPROTO_ISO socket type") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/iso.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 038e292fb194..86a596f2a3fd 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -571,7 +571,7 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb) struct sock *sk; iso_conn_lock(conn); - sk = conn->sk; + sk = iso_sock_hold(conn); iso_conn_unlock(conn); if (!sk) @@ -580,11 +580,15 @@ static void iso_recv_frame(struct iso_conn *conn, struct sk_buff *skb) BT_DBG("sk %p len %d", sk, skb->len); if (sk->sk_state != BT_CONNECTED) - goto drop; + goto drop_put; - if (!sock_queue_rcv_skb(sk, skb)) + if (!sock_queue_rcv_skb(sk, skb)) { + sock_put(sk); return; + } +drop_put: + sock_put(sk); drop: kfree_skb(skb); } From bc08c15746f25f41dd0508b25780d1e84acbb2ef Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Wed, 27 May 2026 04:59:18 +0000 Subject: [PATCH 510/697] Bluetooth: ISO: serialize iso_sock_clear_timer with socket lock commit 4b5f8e608749b7e8fa386c6e4301cf9272595859 upstream. iso_sock_close() calls iso_sock_clear_timer() before acquiring lock_sock(sk). iso_sock_clear_timer() reads iso_pi(sk)->conn twice without the socket lock held: if (!iso_pi(sk)->conn) return; cancel_delayed_work(&iso_pi(sk)->conn->timeout_work); Concurrently, iso_conn_del() executes under lock_sock(sk) and calls iso_chan_del(), which sets iso_pi(sk)->conn to NULL and may result in the final reference to the connection being dropped: CPU0 CPU1 ---- ---- iso_sock_clear_timer() if (conn != NULL) ... lock_sock(sk) iso_chan_del() iso_pi(sk)->conn = NULL cancel_delayed_work(conn) /* NULL deref or UAF */ iso_pi(sk)->conn is not stable across the unlock window, causing a NULL pointer dereference or use-after-free. Serialize iso_sock_clear_timer() with the socket lock by moving it inside lock_sock()/release_sock(), matching the pattern used in iso_conn_del() and all other call sites. Fixes: ccf74f2390d60a2f9a75ef496d2564abb478f46a ("Bluetooth: Add BTPROTO_ISO socket type") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Bilal Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/iso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c index 86a596f2a3fd..9b421e4a2466 100644 --- a/net/bluetooth/iso.c +++ b/net/bluetooth/iso.c @@ -854,8 +854,8 @@ static void __iso_sock_close(struct sock *sk) /* Must be called on unlocked socket. */ static void iso_sock_close(struct sock *sk) { - iso_sock_clear_timer(sk); lock_sock(sk); + iso_sock_clear_timer(sk); __iso_sock_close(sk); release_sock(sk); iso_sock_kill(sk); From e6b78019664dfe37c3dc707f50e7b453d6c7726d Mon Sep 17 00:00:00 2001 From: Pavitra Jha Date: Thu, 21 May 2026 04:04:14 -0400 Subject: [PATCH 511/697] Bluetooth: hci_conn: Fix memory leak in hci_le_big_terminate() commit bfa9d28960ed677d556bdf097073bc3129686229 upstream. hci_le_big_terminate() allocates iso_list_data via kzalloc_obj but returns 0 without freeing it when neither pa_sync_term nor big_sync_term flags are set after evaluating the PA and BIG sync connection state. This early-return path was introduced when hci_le_big_terminate() was refactored to take struct hci_conn instead of raw u8 parameters, adding PA/BIG flag evaluation logic. The existing kfree() on hci_cmd_sync_queue failure does not cover this path. Fixes: a7bcffc673de ("Bluetooth: Add PA_LINK to distinguish BIG sync and PA sync connections") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_conn.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1b63bc2753a2..3be7ee643805 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -803,8 +803,10 @@ static int hci_le_big_terminate(struct hci_dev *hdev, struct hci_conn *conn) d->big_sync_term = true; } - if (!d->pa_sync_term && !d->big_sync_term) + if (!d->pa_sync_term && !d->big_sync_term) { + kfree(d); return 0; + } ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d, terminate_big_destroy); From 2b7651f58670585426938886abdf872bd3fefa3f Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Mon, 25 May 2026 14:51:56 +0800 Subject: [PATCH 512/697] Bluetooth: hci_qca: Use 100 ms SSR delay for rampatch and NVM loading commit fa21e86caba2347e89eb65af926205a36a097c53 upstream. When bt_en is pulled high by hardware, the host does not re-download the firmware after SSR. The controller loads the rampatch and NVM internally. On HMT chip, the rampatch is ~264 KB and the NVM is ~9.4 KB. The loading process takes approximately 70 ms. The previous 50 ms delay is too short, causing the controller to not respond to the reset command sent by the host, which leads to BT initialization failure: Bluetooth: hci0: QCA memdump Done, received 458752, total 458752 Bluetooth: hci0: mem_dump_status: 2 Bluetooth: hci0: Opcode 0x0c03 failed: -110 Increase the delay to 100 ms, which was confirmed as a safe value by the controller, to ensure the controller has finished loading the firmware before the host sends commands. Steps to reproduce: 1. Trigger SSR and wait for SSR to complete: hcitool cmd 0x3f 0c 26 2. Run "bluetoothctl power on" and observe that BT fails to start. Fixes: fce1a9244a0f ("Bluetooth: hci_qca: Fix SSR (SubSystem Restart) fail when BT_EN is pulled up by hw") Cc: stable@vger.kernel.org Reviewed-by: Dmitry Baryshkov Signed-off-by: Shuai Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_qca.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index c0cc04995fc2..acb75199ba49 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1676,8 +1676,8 @@ static void qca_hw_error(struct hci_dev *hdev, u8 code) mod_timer(&qca->tx_idle_timer, jiffies + msecs_to_jiffies(qca->tx_idle_delay)); - /* Controller reset completion time is 50ms */ - msleep(50); + /* Wait for the controller to load the rampatch and NVM. */ + msleep(100); clear_bit(QCA_SSR_TRIGGERED, &qca->flags); clear_bit(QCA_IBS_DISABLED, &qca->flags); From d9019210c8c30d40eb20094274cc647e352f48f7 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Mon, 25 May 2026 18:24:38 +0200 Subject: [PATCH 513/697] Bluetooth: hci_sync: fix UAF in hci_le_create_cis_sync commit bfea6091e0fffb270c20e74384b660910277eb6c upstream. hci_le_create_cis_sync() dereferences conn->conn_timeout after releasing both rcu_read_lock() and hci_dev_lock(hdev). The conn pointer was obtained from an RCU-protected iteration over hdev->conn_hash.list and is not valid once these locks are dropped. A concurrent disconnect can free the hci_conn between the unlock and the dereference, causing a use-after-free read. The cancellation mechanism in hci_conn_del() cannot prevent this because hci_le_create_cis_pending() queues hci_create_cis_sync with data=NULL: hci_cmd_sync_queue(hdev, hci_create_cis_sync, NULL, NULL); While hci_conn_del() dequeues with data=conn: hci_cmd_sync_dequeue(hdev, NULL, conn, NULL); Since NULL != conn, the lookup in _hci_cmd_sync_lookup_entry() never matches, and the pending work item is not cancelled. Fix this by saving conn->conn_timeout into a local variable while the locks are still held, so the stale conn pointer is never dereferenced after unlock. This is the same class of bug as the one fixed by commit 035c25007c9e ("Bluetooth: hci_sync: Fix UAF on le_read_features_complete") which addressed the identical pattern in a different function. This vulnerability was identified using 0sec.ai, an open-source automated security auditing platform (https://github.com/0sec-labs). Fixes: c09b80be6ffc ("Bluetooth: hci_conn: Fix not waiting for HCI_EVT_LE_CIS_ESTABLISHED") Cc: stable@vger.kernel.org Reported-by: Doruk Tan Ozturk Signed-off-by: Doruk Tan Ozturk Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Greg Kroah-Hartman --- net/bluetooth/hci_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c index 277de808ebeb..826be7ff0f56 100644 --- a/net/bluetooth/hci_sync.c +++ b/net/bluetooth/hci_sync.c @@ -6656,6 +6656,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev) DEFINE_FLEX(struct hci_cp_le_create_cis, cmd, cis, num_cis, 0x1f); size_t aux_num_cis = 0; struct hci_conn *conn; + u16 timeout = 0; u8 cig = BT_ISO_QOS_CIG_UNSET; /* The spec allows only one pending LE Create CIS command at a time. If @@ -6726,6 +6727,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev) set_bit(HCI_CONN_CREATE_CIS, &conn->flags); cis->acl_handle = cpu_to_le16(conn->parent->handle); cis->cis_handle = cpu_to_le16(conn->handle); + timeout = conn->conn_timeout; aux_num_cis++; if (aux_num_cis >= cmd->num_cis) @@ -6745,7 +6747,7 @@ int hci_le_create_cis_sync(struct hci_dev *hdev) return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_CREATE_CIS, struct_size(cmd, cis, cmd->num_cis), cmd, HCI_EVT_LE_CIS_ESTABLISHED, - conn->conn_timeout, NULL); + timeout, NULL); } int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle) From 9749db57233b396353ad5dee81eec9d9880c9246 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 26 Apr 2026 21:09:33 -0700 Subject: [PATCH 514/697] Input: xpad - fix out-of-bounds access for Share button commit 6cdc46b38cf146ce81d4831b6472dbf7731849a2 upstream. xpadone_process_packet() receives len directly from urb->actual_length and uses it to index the share-button byte at data[len - 18] or data[len - 26]. Since both len and data[0] are under the device's control, a broken controller can send a GIP_CMD_INPUT packet with actual_length < 18 (e.g. 5 bytes) and reach this code path, causing accesses beyond the actual array. Fix this by calculating the offset and checking bounds against the packet length. Reported-by: Greg Kroah-Hartman Fixes: 4ef46367073b ("Input: xpad - fix Share button on Xbox One controllers") Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/xpad.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index 627e8950e451..c75e1b695dfc 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -1110,10 +1110,10 @@ static void xpadone_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char input_report_key(dev, BTN_START, data[4] & BIT(2)); input_report_key(dev, BTN_SELECT, data[4] & BIT(3)); if (xpad->mapping & MAP_SHARE_BUTTON) { - if (xpad->mapping & MAP_SHARE_OFFSET) - input_report_key(dev, KEY_RECORD, data[len - 26] & BIT(0)); - else - input_report_key(dev, KEY_RECORD, data[len - 18] & BIT(0)); + u32 offset = (xpad->mapping & MAP_SHARE_OFFSET) ? 26 : 18; + + if (len >= offset) + input_report_key(dev, KEY_RECORD, data[len - offset] & BIT(0)); } /* buttons A,B,X,Y */ From 74d6aae1df45d3414178986be743f946988fddf6 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 5 May 2026 20:45:12 +0200 Subject: [PATCH 515/697] parport: Fix race between port and client registration commit ef15ccbb3e8640a723c42ad90eaf81d66ae02017 upstream. The parport subsystem registers port devices before they are fully initialised, resulting in a race condition where client drivers such as lp can attach to ports that are not completely initialised or even being torn down. When the port and client drivers are built as modules and loaded around the same time during boot, this occasionally results in a crash. I was able to make this happen reliably in a VM with a PC-style parallel port by patching parport_pc to fail probing: > --- a/drivers/parport/parport_pc.c > +++ b/drivers/parport/parport_pc.c > @@ -2069,7 +2069,7 @@ static struct parport *__parport_pc_probe_port(unsigned long int base, > if (!p) > goto out3; > > - base_res = request_region(base, 3, p->name); > + base_res = NULL; > if (!base_res) > goto out4; > and then running: while true; do modprobe lp & modprobe parport_pc wait rmmod lp parport_pc done for a few seconds. In the long term I think port registration should be changed to put the call to device_add() inside parport_announce_port(), but since the latter currently cannot fail this will require changing all port drivers. For now, add a flag to indicate whether a port has been "announced" and only try to attach client drivers to ports when the flag is set. Fixes: 6fa45a226897 ("parport: add device-model to parport subsystem") Closes: https://bugs.debian.org/1130365 Closes: https://lore.kernel.org/all/6ba903ad-9897-42bb-8c2d-337385cc3746@molgen.mpg.de/ Cc: stable Signed-off-by: Ben Hutchings Acked-by: Sudip Mukherjee Link: https://patch.msgid.link/afo6uBv68GDevbMD@decadent.org.uk Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/parport/share.c | 11 +++++++++-- include/linux/parport.h | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 427abdf3c4c4..80bdf55eeb9a 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -214,10 +214,14 @@ static void get_lowlevel_driver(void) static int port_check(struct device *dev, void *dev_drv) { struct parport_driver *drv = dev_drv; + struct parport *port; /* only send ports, do not send other devices connected to bus */ - if (is_parport(dev)) - drv->match_port(to_parport_dev(dev)); + if (is_parport(dev)) { + port = to_parport_dev(dev); + if (test_bit(PARPORT_ANNOUNCED, &port->devflags)) + drv->match_port(port); + } return 0; } @@ -532,6 +536,7 @@ void parport_announce_port(struct parport *port) if (slave) attach_driver_chain(slave); } + set_bit(PARPORT_ANNOUNCED, &port->devflags); mutex_unlock(®istration_lock); } EXPORT_SYMBOL(parport_announce_port); @@ -561,6 +566,8 @@ void parport_remove_port(struct parport *port) mutex_lock(®istration_lock); + clear_bit(PARPORT_ANNOUNCED, &port->devflags); + /* Spread the word. */ detach_driver_chain(port); diff --git a/include/linux/parport.h b/include/linux/parport.h index 464c2ad28039..f64cb0676e3b 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -240,6 +240,7 @@ struct parport { unsigned long devflags; #define PARPORT_DEVPROC_REGISTERED 0 +#define PARPORT_ANNOUNCED 1 struct pardevice *proc_device; /* Currently register proc device */ struct list_head full_list; From f2f2671e32c55e93247febfad3d71973a9f5046e Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 3 Apr 2026 18:18:58 +0000 Subject: [PATCH 516/697] rust_binder: Avoid holding lock when dropping delivered_death commit f6d8fea9e3953151a4adb4f603503dc3dc9c69da upstream. In 6c37bebd8c926, we switched to looping over the list and dropping each individual node, ostensibly without the lock held in the loop body. If the kernel were using Rust Edition 2024, the comment would be accurate, and the lock would not be held across the drop. However, the kernel is currently using 2021, so tail expression lifetime extension results in the lock being held across the drop. Explicitly binding the expression result to a variable makes the lockguard no longer part of a tail expression, causing the lock to be dropped before entering the loop body. This was detected via `CONFIG_PROVE_LOCKING` identifying an invalid wait context at the drop site. Reported-by: David Stevens Signed-off-by: Matthew Maurer Cc: stable Fixes: 6c37bebd8c92 ("rust_binder: avoid mem::take on delivered_deaths") Reviewed-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260403-lockhold-v1-1-c332b56cd8ae@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 7d66d8cba932..5e95aee95347 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1366,7 +1366,12 @@ impl Process { // Clear delivered_deaths list. // // Scope ensures that MutexGuard is dropped while executing the body. - while let Some(delivered_death) = { self.inner.lock().delivered_deaths.pop_front() } { + while let Some(delivered_death) = { + // Explicitly bind to avoid tail expression lifetime extension of the lockguard + // Can be removed when the kernel moves to edition 2024 + let maybe_death = self.inner.lock().delivered_deaths.pop_front(); + maybe_death + } { drop(delivered_death); } From 667599e718329b4e8396e6958aa16295554278ee Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 14 Apr 2026 12:02:34 +0000 Subject: [PATCH 517/697] rust_binder: avoid calling pending_oneway_finished() on TF_UPDATE_TXN commit 4c19719eb8b8df08c5bec7c499f73ddaea6f09fc upstream. When an outdated transaction is removed from `oneway_todo` due to `TF_UPDATE_TXN`, its `Allocation` is dropped. The current implementation of `Allocation::drop` calls `pending_oneway_finished()`, assuming the transaction was executed. This leads to premature execution of the next queued one-way transaction. Fix this by taking the `oneway_node` from the `Allocation` of the outdated transaction before it is dropped. This prevents `Allocation::drop` from signaling completion. We do not call `take_oneway_node()` from `Transaction::cancel` because it's actually correct to call `pending_oneway_finished()` on cancel if the transaction did not come from `oneway_todo`. This ensures that if `BINDER_THREAD_EXIT` is invoked and cancels a oneway transaction, then the next transaction is taken from `oneway_todo`. This bug does not lead to any issues in the kernel, but may lead to Binder delivering transactions to userspace earlier than userspace expected to receive them. Cc: stable Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") Assisted-by: Antigravity:gemini Signed-off-by: Alice Ryhl Acked-by: Carlos Llamas Link: https://patch.msgid.link/20260414-tf-update-txn-fix-v1-1-d2b83303acc9@google.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 8 ++++++++ drivers/android/binder/transaction.rs | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 7f65a9c3a0e5..d9113e9b98b2 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -160,6 +160,14 @@ impl Allocation { self.get_or_init_info().target_node = Some(target_node); } + pub(crate) fn take_oneway_node(&mut self) -> Option> { + if let Some(info) = self.allocation_info.as_mut() { + info.oneway_node.take() + } else { + None + } + } + /// Reserve enough space to push at least `num_fds` fds. pub(crate) fn info_add_fd_reserve(&mut self, num_fds: usize) -> Result { self.get_or_init_info() diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 4bd3c0e417eb..50bd1cee0f1a 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -239,7 +239,8 @@ impl Transaction { /// Not used for replies. pub(crate) fn submit(self: DLArc) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. - let mut _t_outdated; + let _t_outdated; + let _oneway_node; let oneway = self.flags & TF_ONE_WAY != 0; let process = self.to.clone(); @@ -255,6 +256,14 @@ impl Transaction { if let Some(t_outdated) = target_node.take_outdated_transaction(&self, &mut process_inner) { + let mut alloc_guard = t_outdated.allocation.lock(); + if let Some(alloc) = (*alloc_guard).as_mut() { + // Take the oneway node to prevent `Allocation::drop` from calling + // `pending_oneway_finished()`, which would be incorrect as this + // transaction is not being submitted. + _oneway_node = alloc.take_oneway_node(); + } + drop(alloc_guard); // Save the transaction to be dropped after locks are released. _t_outdated = t_outdated; } From 625153b917bc490c8f05ea4c84463e42ec1e590c Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Fri, 22 May 2026 17:13:58 +0800 Subject: [PATCH 518/697] USB: cdc-acm: Fix bit overlap and move quirk definitions to header commit 5eb070769ea5e18405535609d1d3f6886f3755bd upstream. The VENDOR_CLASS_DATA_IFACE and ALWAYS_POLL_CTRL quirk flags added in commit f58752ebcb35 ("USB: cdc-acm: Add quirks for Yoga Book 9 14IAH10 INGENIC touchscreen") were placed inside the acm_ctrl_msg() function rather than in the header with the other quirk flags. Then, their values (BIT(9) and BIT(10)) collided with NO_UNION_12 which is already BIT(9). Move the definitions to drivers/usb/class/cdc-acm.h where they belong and shift them to BIT(10) and BIT(11) to avoid the overlap. Fixes: f58752ebcb35 ("USB: cdc-acm: Add quirks for Yoga Book 9 14IAH10 INGENIC touchscreen") Cc: stable Signed-off-by: Wentao Guan Link: https://patch.msgid.link/20260522091357.1301196-1-guanwentao@uniontech.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 2 -- drivers/usb/class/cdc-acm.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index d05b8806124a..730ea34cb744 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); -#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ -#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 25fd5329a878..01f448a783c0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -115,3 +115,5 @@ struct acm { #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) #define NO_UNION_12 BIT(9) +#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */ From b1fc4a83dd4495d4c381dcdd3d19c6520e5fb62c Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Tue, 26 May 2026 15:46:40 +0800 Subject: [PATCH 519/697] KVM: arm64: PMU: Preserve AArch32 counter low bits commit 1750ad1388e03fb27068cd1f22c9c8b4590fe936 upstream. AArch32 writes to PMU event counters cannot update the top 32 bits, even when PMUv3p5 makes the counters 64-bit. KVM therefore needs to preserve the existing high half and only update the low half written by the guest, unless the caller explicitly forces a full reset through PMCR.P. The current code masks @val down to the old high half before taking lower_32_bits(val), which means the low half is always zero. As a result, AArch32 writes to event counters discard the guest-provided low 32 bits instead of storing them. Build the new value from the old high 32 bits and the low 32 bits of the value supplied by the guest. Fixes: 26d2d0594d70 ("KVM: arm64: PMU: Do not let AArch32 change the counters' top 32 bits") Signed-off-by: Qiang Ma Signed-off-by: Marc Zyngier Link: https://patch.msgid.link/20260526074640.791991-1-maqianga@uniontech.com Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/pmu-emul.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index b03dbda7f1ab..3d4515154b5d 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -174,8 +174,8 @@ static void kvm_pmu_set_pmc_value(struct kvm_pmc *pmc, u64 val, bool force) * action is to use PMCR.P, which will reset them to * 0 (the only use of the 'force' parameter). */ - val = __vcpu_sys_reg(vcpu, reg) & GENMASK(63, 32); - val |= lower_32_bits(val); + val = (__vcpu_sys_reg(vcpu, reg) & GENMASK(63, 32)) | + lower_32_bits(val); } __vcpu_assign_sys_reg(vcpu, reg, val); From ec62e8480e824cdb866cba21d2340111f53953be Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 15 May 2026 10:15:36 -0700 Subject: [PATCH 520/697] KVM: SVM: Flush the current TLB when transitioning from xAVIC => x2AVIC commit a9e18aa3263f356edae305e29830e5fe63d8597a upstream. Flush the current TLB when xAVIC *or* x2AVIC is activated, as KVM is (apparently) responsible for purging TLB entries when transitioning from xAVIC to x2AVIC. The APM says a whole lot of nothing about TLB flushing with respect to (x2)AVIC, but empirical data strongly suggests hardware also does a whole lot of nothing. Failure to flush the TLB when enabling x2AVIC can lead to guest accesses to the APIC base address getting incorrectly redirected to the virtual APIC page. The flaw most visibly manifests as failures in KVM-Unit-Test's verify_disabled_apic_mmio() testcase when x2APIC is enabled (though for reasons unknown, the test only reliably fails with EFI builds). Fixes: 0ccf3e7cb95a ("KVM: SVM: Flush the "current" TLB when activating AVIC") Fixes: 4d1d7942e36a ("KVM: SVM: Introduce logic to (de)activate x2AVIC mode") Cc: stable@vger.kernel.org Cc: Naveen N Rao (AMD) Link: https://patch.msgid.link/20260515171536.1841645-1-seanjc@google.com Signed-off-by: Sean Christopherson Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/avic.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index ba844088581f..d7531e7aff4d 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -186,6 +186,35 @@ static void avic_activate_vmcb(struct vcpu_svm *svm) svm_clr_intercept(svm, INTERCEPT_CR8_WRITE); + /* + * Flush the TLB when enabling (x2)AVIC and when transitioning between + * xAVIC and x2AVIC, as the CPU may have inserted a TLB entry for the + * "wrong" mapping. + * + * KVM uses a per-VM "scratch" page to back the APIC memslot, because + * KVM also uses per-VM page tables *and* maintains the page table (NPT + * or shadow page) mappings for said memslot even if one or more vCPUs + * have their local APIC hardware-disabled or are in x2APIC mode, i.e. + * even if one or more vCPUs' APIC MMIO BAR is effectively disabled. + * + * If xAVIC is fully enabled, hardware ignores the physical address in + * KVM's page tables, i.e. in the leaf SPTE for the APIC memslot, and + * instead redirects the access to the AVIC backing page, i.e. to the + * vCPU's virtual APIC page. If xAVIC is not enabled (APIC is either + * hardware-disabled or in x2APIC mode), then guest accesses will use + * the page table mapping verbatim, i.e. will access the per-VM scratch + * page, as normal memory. + * + * In both cases, the CPU is allowed to cache TLB entries for the APIC + * base GPA. So, KVM needs to flush the TLB when enabling xAVIC, as + * accesses need to be redirected to the virtual APIC page, but the TLB + * may contain entries pointing at the scratch page. KVM also needs to + * flush the TLB when enabling x2AVIC, as accesses need to go to the + * scratch page, but the TLB may contain entries tagged as xAVIC, i.e. + * entries pointing to the vCPU's virtual APIC page. + */ + kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, &svm->vcpu); + /* * Note: KVM supports hybrid-AVIC mode, where KVM emulates x2APIC MSR * accesses, while interrupt injection to a running vCPU can be @@ -199,12 +228,6 @@ static void avic_activate_vmcb(struct vcpu_svm *svm) /* Disabling MSR intercept for x2APIC registers */ avic_set_x2apic_msr_interception(svm, false); } else { - /* - * Flush the TLB, the guest may have inserted a non-APIC - * mapping into the TLB while AVIC was disabled. - */ - kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, &svm->vcpu); - /* Enabling MSR intercept for x2APIC registers */ avic_set_x2apic_msr_interception(svm, true); } From c9b4198fbc6ed99a9da4bee9f74bb730f926c9ae Mon Sep 17 00:00:00 2001 From: Michael Roth Date: Fri, 1 May 2026 13:22:26 -0700 Subject: [PATCH 521/697] KVM: SEV: Require in-GHCB scratch area if GHCB v2+ is in use commit db3f2195d29344a3cf1e9dd9ab7f21ced7308cf7 upstream. As per the GHCB spec, when using GHCB v2+ require the software scratch area to reside in the GHCB's shared buffer. Note, things like Page State Change (PSC) requests _rely_ on this behavior, as the guest can't provide a length when making the request, i.e. the size of the guest payload is bounded by the size of the shared buffer. Failure to force usage of the GHCB, and a slew of other flaws, lets a malicious SNP guest corrupt host kernel heap memory, and leak host heap layout information. setup_vmgexit_scratch() allocates a buffer via kvzalloc(exit_info_2), where exit_info_2 is guest-controlled. With exit_info_2=24, this yields a 24-byte allocation in kmalloc-cg-32 (32-byte slab objects). The buffer holds an 8-byte psc_hdr followed by 8-byte psc_entry structs, so only entries[0] and entries[1] are in-bounds. snp_begin_psc() validates end_entry against VMGEXIT_PSC_MAX_COUNT (253) but NOT against the actual buffer size: idx_end = hdr->end_entry; if (idx_end >= VMGEXIT_PSC_MAX_COUNT) { // checks 253, not buffer snp_complete_psc(svm, ...); return 1; } for (idx = idx_start; idx <= idx_end; idx++) { entry_start = entries[idx]; // OOB when idx >= 2 The guest sets end_entry=10+, causing the host to iterate entries[2+] which are OOB into adjacent slab objects. For each OOB entry: - The host reads 8 bytes (OOB READ / info leak oracle) - If the data passes PSC validation, __snp_complete_one_psc() writes cur_page = 1 or 512 into the entry (OOB WRITE, sev.c:3806) - If validation fails, the error response reveals whether adjacent memory is zero vs non-zero (information disclosure to guest) The guest controls allocation size (exit_info_2), entry range (cur_entry/end_entry), and can fire unlimited VMGEXITs to repeatedly hit different slab positions. By exploiting the variety of bugs, a malicious SEV-SNP guest can: - OOB read adjacent kmalloc-cg-32 objects (heap layout disclosure) - OOB write cur_page bits into adjacent objects (heap corruption) - Trigger use-after-free conditions across VMGEXITs E.g. with KASAN enabled, a single insmod of the PoC guest module produces 73 KASAN reports: BUG: KASAN: slab-out-of-bounds in snp_begin_psc+0x126/0x890 Read of size 8 at addr ffff888219ffb5e0 by task qemu-system-x86/2199 BUG: KASAN: slab-out-of-bounds in snp_begin_psc+0x468/0x890 Write of size 8 at addr ffff888351566648 by task qemu-system-x86/2199 The buggy address belongs to the object at ffff888XXXXXXXXX which belongs to the cache kmalloc-cg-32 of size 32 The buggy address is located N bytes to the right of allocated 32-byte region [ffff888XXXXXXXXX, ffff888XXXXXXXXX) Breakdown: 62 slab-out-of-bounds (reads + writes past allocation) 7 slab-use-after-free 4 use-after-free All credit to Stan for the wonderful description and reproducer! Reported-by: Stan Shaw Cc: Michael Roth Cc: Tom Lendacky Cc: Peter Gonda Cc: Jacky Li Fixes: 4af663c2f64a ("KVM: SEV: Allow per-guest configuration of GHCB protocol version") Cc: stable@vger.kernel.org Signed-off-by: Michael Roth [sean: write changelog] Reviewed-by: Tom Lendacky Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-2-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 4b778e71b4c3..eda1dc4d3c4a 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3651,6 +3651,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) scratch_va = (void *)svm->sev_es.ghcb; scratch_va += (scratch_gpa_beg - control->ghcb_gpa); } else { + /* GHCB v2 requires the scratch area to be within the GHCB. */ + if (to_kvm_sev_info(svm->vcpu.kvm)->ghcb_version >= 2) + goto e_scratch; + /* * The guest memory must be read into a kernel buffer, so * limit the size From 2254972d4d69e279ba4e87bf0968eb08ad0d3c92 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:29 -0700 Subject: [PATCH 522/697] KVM: SEV: Ignore Port I/O requests of length '0' commit 3988bd2723de407ae90fa7a6f6029b4e60238c58 upstream. Explicitly ignore Port I/O requests of length '0' (or count '0'), so that setting up the software scratch area (and other code) doesn't have to worry about underflowing the length, and to allow for WARNing on trying to configure the scratch area with len==0. Fixes: 291bd20d5d88 ("KVM: SVM: Add initial support for a VMGEXIT VMEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-5-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index eda1dc4d3c4a..6e82149c2c35 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4499,6 +4499,11 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) control->exit_info_1, control->exit_info_2); ret = -EINVAL; break; + case SVM_EXIT_IOIO: + if (!((control->exit_info_1 & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT)) + return 1; + + fallthrough; default: ret = svm_invoke_exit_handler(vcpu, exit_code); } @@ -4519,6 +4524,9 @@ int sev_es_string_io(struct vcpu_svm *svm, int size, unsigned int port, int in) if (unlikely(check_mul_overflow(count, size, &bytes))) return -EINVAL; + if (!bytes) + return 1; + r = setup_vmgexit_scratch(svm, in, bytes); if (r) return r; From e4ab26f81a632a971936db0301be52da253300dd Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:30 -0700 Subject: [PATCH 523/697] KVM: SEV: Use the size of the PSC header as the minimum size for PSC requests commit 2be54670bdc017004c4a4b8bddb6ff02ebe7dbe2 upstream. When handling a Page State Change (PSC) #VMGEXIT use the size of the PSC header as the minimum size for the scratch area. Per the GHCB spec, PSC requests do NOT provide the length, i.e. using control->exit_info_2 for the length is completely made up behavior. The existing code "works", e.g. even though Linux-as-a-guest always passes '0', because KVM doesn't do anything with the length when the request is in the GHCB's shared buffer. Use the header as the min length. Once the header is retrieved, KVM can use the specified indices to compute the full size of the request. Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-6-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 6e82149c2c35..7b49df6ffaf0 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -4473,7 +4473,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) vcpu->run->system_event.data[0] = control->ghcb_gpa; break; case SVM_VMGEXIT_PSC: - ret = setup_vmgexit_scratch(svm, true, control->exit_info_2); + ret = setup_vmgexit_scratch(svm, true, sizeof(struct psc_hdr)); if (ret) break; From 5300aedbee5618edf16527dc539fea6001916a40 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:32 -0700 Subject: [PATCH 524/697] KVM: SEV: WARN if KVM attempts to setup scratch area with min_len==0 commit f185e05dce6f170f83c4ba602e969b1c3c7a22e6 upstream. Now that all paths in KVM properly validate the length needed for the scratch area, and are guaranteed to pass in a non-zero length, WARN if KVM attempts to configured the scratch area with min_len==0 to guard against future bugs. Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-8-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 7b49df6ffaf0..ffbbc8e6f302 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3617,6 +3617,9 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) u64 scratch_gpa_beg, scratch_gpa_end; void *scratch_va; + if (WARN_ON_ONCE(!min_len)) + goto e_scratch; + scratch_gpa_beg = svm->sev_es.sw_scratch; if (!scratch_gpa_beg) { pr_err("vmgexit: scratch gpa not provided\n"); From 9f0a9e780f02c02d025a190f1885e1d1d73b87bd Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:31 -0700 Subject: [PATCH 525/697] KVM: SEV: Compute the correct max length of the in-GHCB scratch area commit 5867d7e202e09f037cefe77f7af4413c7c0fa088 upstream. When setting the length of the GHCB scratch area, and the area is in the GHCB shared buffer, set the effective length of the scratch area to the max possible size given the start of the guest-provided pointer, and the end of the shared buffer. The code was "fine" when first introduced, as KVM doesn't consult the length of the buffer when emulating MMIO, because the passed in @len always specifies the *max* size required. But for PSC requests, the incoming @len is just the minimum length (to process the header), and KVM needs to know the full size of the scratch area to avoid buffer overflows (spoiler alert). Opportunistically rename @len => @min_len to better reflect its role. Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-7-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index ffbbc8e6f302..4b516a63d924 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3610,7 +3610,7 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu) } #define GHCB_SCRATCH_AREA_LIMIT (16ULL * PAGE_SIZE) -static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) +static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 min_len) { struct vmcb_control_area *control = &svm->vmcb->control; u64 ghcb_scratch_beg, ghcb_scratch_end; @@ -3626,10 +3626,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) goto e_scratch; } - scratch_gpa_end = scratch_gpa_beg + len; + scratch_gpa_end = scratch_gpa_beg + min_len; if (scratch_gpa_end < scratch_gpa_beg) { pr_err("vmgexit: scratch length (%#llx) not valid for scratch address (%#llx)\n", - len, scratch_gpa_beg); + min_len, scratch_gpa_beg); goto e_scratch; } @@ -3653,6 +3653,8 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) scratch_va = (void *)svm->sev_es.ghcb; scratch_va += (scratch_gpa_beg - control->ghcb_gpa); + + svm->sev_es.ghcb_sa_len = ghcb_scratch_end - scratch_gpa_beg; } else { /* GHCB v2 requires the scratch area to be within the GHCB. */ if (to_kvm_sev_info(svm->vcpu.kvm)->ghcb_version >= 2) @@ -3662,16 +3664,16 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) * The guest memory must be read into a kernel buffer, so * limit the size */ - if (len > GHCB_SCRATCH_AREA_LIMIT) { + if (min_len > GHCB_SCRATCH_AREA_LIMIT) { pr_err("vmgexit: scratch area exceeds KVM limits (%#llx requested, %#llx limit)\n", - len, GHCB_SCRATCH_AREA_LIMIT); + min_len, GHCB_SCRATCH_AREA_LIMIT); goto e_scratch; } - scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT); + scratch_va = kvzalloc(min_len, GFP_KERNEL_ACCOUNT); if (!scratch_va) return -ENOMEM; - if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, len)) { + if (kvm_read_guest(svm->vcpu.kvm, scratch_gpa_beg, scratch_va, min_len)) { /* Unable to copy scratch area from guest */ pr_err("vmgexit: kvm_read_guest for scratch area failed\n"); @@ -3687,11 +3689,10 @@ static int setup_vmgexit_scratch(struct vcpu_svm *svm, bool sync, u64 len) */ svm->sev_es.ghcb_sa_sync = sync; svm->sev_es.ghcb_sa_free = true; + svm->sev_es.ghcb_sa_len = min_len; } svm->sev_es.ghcb_sa = scratch_va; - svm->sev_es.ghcb_sa_len = len; - return 0; e_scratch: From 75c8d1d7291268b479794fba5808971dc2f5eaf3 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:34 -0700 Subject: [PATCH 526/697] KVM: SEV: Check PSC request indices against the actual size of the buffer commit 121d88de56bc5c0ba0ce2f6381af67f948a7e7c1 upstream. When processing Page State Change (PSC) requests, validate the PSC buffer against the effective size of the scratch area, which could be less than the maximum size if the guest provided a pointer that isn't exactly at the start of the GHCB shared buffer. Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-10-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 4b516a63d924..05cb0dafaa95 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3850,7 +3850,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) struct kvm_vcpu *vcpu = &svm->vcpu; struct psc_hdr *hdr = &psc->hdr; struct psc_entry entry_start; - u16 idx, idx_start, idx_end; + u16 idx, idx_start, idx_end, max_nr_entries; int npages; bool huge; u64 gfn; @@ -3860,6 +3860,19 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) return 1; } + /* + * GHCB v2 requires the scratch area to reside within the GHCB itself, + * and PSC requests are only supported for GHCB v2+. Thus it should be + * impossible to exceed the max PSC entry count (which is derived from + * the size of the shared GHCB buffer). + */ + max_nr_entries = (sev_es->ghcb_sa_len - sizeof(struct psc_hdr)) / + sizeof(struct psc_entry); + if (WARN_ON_ONCE(max_nr_entries > VMGEXIT_PSC_MAX_COUNT)) { + snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); + return 1; + } + next_range: /* There should be no other PSCs in-flight at this point. */ if (WARN_ON_ONCE(svm->sev_es.psc_inflight)) { @@ -3875,7 +3888,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) idx_start = hdr->cur_entry; idx_end = hdr->end_entry; - if (idx_end >= VMGEXIT_PSC_MAX_COUNT) { + if (idx_end >= max_nr_entries) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR); return 1; } From b1dfaa6f7a957726a6800135be3659fbe4bbf2a4 Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:35 -0700 Subject: [PATCH 527/697] KVM: SEV: Use READ_ONCE() when reading entries/indices from PSC buffer commit c8cc238093ca6c99267032f6cfe78f59389f3157 upstream. Use READ_ONCE() when reading entries/indices from the guest-accessible Page State Change buffer to defend against TOCTOU bugs. Don't bother with READ_ONCE()/WRITE_ONCE() for cases where KVM is writing (and not consuming the result!), as the guest isn't supposed to touch the buffer while it's being processed. I.e. using READ_ONCE() is all about protecting against misbehaving guests. Fixes: 9b54e248d264 ("KVM: SEV: Add support to handle Page State Change VMGEXIT") Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-11-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 05cb0dafaa95..99c3b080e0b7 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3820,9 +3820,9 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm) */ for (idx = svm->sev_es.psc_idx; svm->sev_es.psc_inflight; svm->sev_es.psc_inflight--, idx++) { - struct psc_entry *entry = &entries[idx]; + struct psc_entry entry = READ_ONCE(entries[idx]); - entry->cur_page = entry->pagesize ? 512 : 1; + entries[idx].cur_page = entry.pagesize ? 512 : 1; } hdr->cur_entry = idx; @@ -3885,8 +3885,8 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) * validation, so take care to only use validated copies of values used * for things like array indexing. */ - idx_start = hdr->cur_entry; - idx_end = hdr->end_entry; + idx_start = READ_ONCE(hdr->cur_entry); + idx_end = READ_ONCE(hdr->end_entry); if (idx_end >= max_nr_entries) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_INVALID_HDR); @@ -3895,7 +3895,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) /* Find the start of the next range which needs processing. */ for (idx = idx_start; idx <= idx_end; idx++, hdr->cur_entry++) { - entry_start = entries[idx]; + entry_start = READ_ONCE(entries[idx]); gfn = entry_start.gfn; huge = entry_start.pagesize; @@ -3939,7 +3939,7 @@ static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) * KVM_HC_MAP_GPA_RANGE exit. */ while (++idx <= idx_end) { - struct psc_entry entry = entries[idx]; + struct psc_entry entry = READ_ONCE(entries[idx]); if (entry.operation != entry_start.operation || entry.gfn != entry_start.gfn + npages || From bbd989d6fd3667d635c9afdeda6e05a2237e418c Mon Sep 17 00:00:00 2001 From: Sean Christopherson Date: Fri, 1 May 2026 13:22:33 -0700 Subject: [PATCH 528/697] KVM: SEV: Don't explicitly pass PSC buffer to snp_begin_psc() commit ebe4b2dc9cfbfb2d8f665667c4d08f4c6c9bec05 upstream. Stop explicitly passing the PSC buffer to snp_begin_psc(): it *must* be the scratch area. This will allow fixing a variety of bugs without further complicating the code. No functional change intended. Cc: stable@vger.kernel.org Reviewed-by: Tom Lendacky Reviewed-by: Michael Roth Signed-off-by: Sean Christopherson Message-ID: <20260501202250.2115252-9-seanjc@google.com> Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/svm/sev.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 99c3b080e0b7..aa9ef645cfa6 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -3789,7 +3789,7 @@ struct psc_buffer { struct psc_entry entries[]; } __packed; -static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc); +static int snp_begin_psc(struct vcpu_svm *svm); static void snp_complete_psc(struct vcpu_svm *svm, u64 psc_ret) { @@ -3831,7 +3831,6 @@ static void __snp_complete_one_psc(struct vcpu_svm *svm) static int snp_complete_one_psc(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); - struct psc_buffer *psc = svm->sev_es.ghcb_sa; if (vcpu->run->hypercall.ret) { snp_complete_psc(svm, VMGEXIT_PSC_ERROR_GENERIC); @@ -3841,11 +3840,13 @@ static int snp_complete_one_psc(struct kvm_vcpu *vcpu) __snp_complete_one_psc(svm); /* Handle the next range (if any). */ - return snp_begin_psc(svm, psc); + return snp_begin_psc(svm); } -static int snp_begin_psc(struct vcpu_svm *svm, struct psc_buffer *psc) +static int snp_begin_psc(struct vcpu_svm *svm) { + struct vcpu_sev_es_state *sev_es = &svm->sev_es; + struct psc_buffer *psc = sev_es->ghcb_sa; struct psc_entry *entries = psc->entries; struct kvm_vcpu *vcpu = &svm->vcpu; struct psc_hdr *hdr = &psc->hdr; @@ -4494,7 +4495,7 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu) if (ret) break; - ret = snp_begin_psc(svm, svm->sev_es.ghcb_sa); + ret = snp_begin_psc(svm); break; case SVM_VMGEXIT_AP_CREATION: ret = sev_snp_ap_creation(svm); From bb1b43e8a7ede56b6beed0e6d73023ade753e6f2 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 16 May 2026 04:34:14 +0900 Subject: [PATCH 529/697] Disable -Wattribute-alias for clang-23 and newer commit 175db11786bde9061db526bf1ac5107d915f5163 upstream. Clang recently added support for -Wattribute-alias [1], which results in the same warnings that necessitated commit bee20031772a ("disable -Wattribute-alias warning for SYSCALL_DEFINEx()") for GCC. kernel/time/itimer.c:325:1: error: alias and aliasee have different types 'long (unsigned int)' and 'long (typeof (__builtin_choose_expr((__builtin_types_compatible_p(typeof ((unsigned int)0), typeof (0LL)) || __builtin_types_compatible_p(typeof ((unsigned int)0), typeof (0ULL))), 0LL, 0L)))' (aka 'long (long)') [-Werror,-Wattribute-alias] 325 | SYSCALL_DEFINE1(alarm, unsigned int, seconds) | ^ include/linux/syscalls.h:225:36: note: expanded from macro 'SYSCALL_DEFINE1' 225 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) | ^ include/linux/syscalls.h:236:2: note: expanded from macro 'SYSCALL_DEFINEx' 236 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) | ^ include/linux/syscalls.h:251:18: note: expanded from macro '__SYSCALL_DEFINEx' 251 | __attribute__((alias(__stringify(__se_sys##name)))); \ | ^ kernel/time/itimer.c:325:1: note: aliasee is declared here include/linux/syscalls.h:225:36: note: expanded from macro 'SYSCALL_DEFINE1' 225 | #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) | ^ include/linux/syscalls.h:236:2: note: expanded from macro 'SYSCALL_DEFINEx' 236 | __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) | ^ include/linux/syscalls.h:255:18: note: expanded from macro '__SYSCALL_DEFINEx' 255 | asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ | ^ :16:1: note: expanded from here 16 | __se_sys_alarm | ^ Disable the warnings in the same way for clang-23 and newer. Disable the warning about unknown warning options to avoid breaking the build for versions of clang-23 that do not have -Wattribute-alias, such as ones deployed by vendors like Android or CI systems or when bisecting LLVM between llvmorg-23-init and release/23.x. Cc: stable@vger.kernel.org Closes: https://github.com/ClangBuiltLinux/linux/issues/2163 Link: https://github.com/llvm/llvm-project/commit/40da6920a0d71d49dfa2392b09153600b0759f5e [1] Link: https://patch.msgid.link/20260515-syscall-disable-attribute-alias-for-clang-v1-1-9a9d95d41df6@kernel.org Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- arch/riscv/include/asm/syscall_wrapper.h | 4 ++++ include/linux/compat.h | 4 ++++ include/linux/compiler-clang.h | 6 ++++++ include/linux/compiler_types.h | 4 ++++ include/linux/syscalls.h | 4 ++++ 5 files changed, 22 insertions(+) diff --git a/arch/riscv/include/asm/syscall_wrapper.h b/arch/riscv/include/asm/syscall_wrapper.h index ac80216549ff..226289c3b5c8 100644 --- a/arch/riscv/include/asm/syscall_wrapper.h +++ b/arch/riscv/include/asm/syscall_wrapper.h @@ -32,6 +32,10 @@ asmlinkage long __riscv_sys_ni_syscall(const struct pt_regs *); __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments"); \ + __diag_ignore(clang, 23, "-Wunknown-warning-option", \ + "Avoid breaking versions without -Wattribute-alias"); \ + __diag_ignore(clang, 23, "-Wattribute-alias", \ + "Type aliasing is used to sanitize syscall arguments"); \ static long __se_##prefix##name(ulong, ulong, ulong, ulong, ulong, ulong, \ ulong) \ __attribute__((alias(__stringify(___se_##prefix##name)))); \ diff --git a/include/linux/compat.h b/include/linux/compat.h index 56cebaff0c91..8da0a15c95f4 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -72,6 +72,10 @@ __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ + __diag_ignore(clang, 23, "-Wunknown-warning-option", \ + "Avoid breaking versions without -Wattribute-alias"); \ + __diag_ignore(clang, 23, "-Wattribute-alias", \ + "Type aliasing is used to sanitize syscall arguments"); \ asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_compat_sys##name)))); \ ALLOW_ERROR_INJECTION(compat_sys##name, ERRNO); \ diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index cccc72fd336b..2e3972058855 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -131,6 +131,12 @@ #define __diag_str(s) __diag_str1(s) #define __diag(s) _Pragma(__diag_str(clang diagnostic s)) +#if CONFIG_CLANG_VERSION >= 230000 +#define __diag_clang_23(s) __diag(s) +#else +#define __diag_clang_23(s) +#endif + #define __diag_clang_13(s) __diag(s) #define __diag_ignore_all(option, comment) \ diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 2998af80cbef..2f18b8a01afe 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -636,6 +636,10 @@ struct ftrace_likely_data { #define __diag_GCC(version, severity, string) #endif +#ifndef __diag_clang +#define __diag_clang(version, severity, string) +#endif + #define __diag_push() __diag(push) #define __diag_pop() __diag(pop) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 66c06fcdfe19..a3e3c8f3dd65 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -247,6 +247,10 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event) __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ + __diag_ignore(clang, 23, "-Wunknown-warning-option", \ + "Avoid breaking versions without -Wattribute-alias");\ + __diag_ignore(clang, 23, "-Wattribute-alias", \ + "Type aliasing is used to sanitize syscall arguments");\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ ALLOW_ERROR_INJECTION(sys##name, ERRNO); \ From 0ee771fff32e8b4c3f531deb1e408901fb4b6700 Mon Sep 17 00:00:00 2001 From: Christofer Jonason Date: Wed, 4 Mar 2026 10:07:27 +0100 Subject: [PATCH 530/697] iio: adc: xilinx-xadc: Fix sequencer mode in postdisable for dual mux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 852534744c2d35626a604f128ff0b8ec12805591 upstream. xadc_postdisable() unconditionally sets the sequencer to continuous mode. For dual external multiplexer configurations this is incorrect: simultaneous sampling mode is required so that ADC-A samples through the mux on VAUX[0-7] while ADC-B simultaneously samples through the mux on VAUX[8-15]. In continuous mode only ADC-A is active, so VAUX[8-15] channels return incorrect data. Since postdisable is also called from xadc_probe() to set the initial idle state, the wrong sequencer mode is active from the moment the driver loads. The preenable path already uses xadc_get_seq_mode() which returns SIMULTANEOUS for dual mux. Fix postdisable to do the same. Fixes: bdc8cda1d010 ("iio:adc: Add Xilinx XADC driver") Cc: stable@vger.kernel.org Signed-off-by: Christofer Jonason Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Reviewed-by: Salih Erim Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/xilinx-xadc-core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index e257c1b94a5f..3980dfacbcd7 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -817,6 +817,7 @@ static int xadc_postdisable(struct iio_dev *indio_dev) { struct xadc *xadc = iio_priv(indio_dev); unsigned long scan_mask; + int seq_mode; int ret; int i; @@ -824,6 +825,12 @@ static int xadc_postdisable(struct iio_dev *indio_dev) for (i = 0; i < indio_dev->num_channels; i++) scan_mask |= BIT(indio_dev->channels[i].scan_index); + /* + * Use the correct sequencer mode for the idle state: simultaneous + * mode for dual external mux configurations, continuous otherwise. + */ + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + /* Enable all channels and calibration */ ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); if (ret) @@ -834,11 +841,11 @@ static int xadc_postdisable(struct iio_dev *indio_dev) return ret; ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, - XADC_CONF1_SEQ_CONTINUOUS); + seq_mode); if (ret) return ret; - return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); + return xadc_power_adc_b(xadc, seq_mode); } static int xadc_preenable(struct iio_dev *indio_dev) From 2ce5ca7824a18c36fdd817398b881c3777b7325e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 14 Apr 2026 13:30:06 +0100 Subject: [PATCH 531/697] iio: adc: npcm: fix unbalanced clk_disable_unprepare() commit 0d42e2c0bd6ceb89e44c6e065f9bdf9b1df3ef0c upstream. The driver acquired the ADC clock with devm_clk_get() and read its rate, but never called clk_prepare_enable(). The probe error path and npcm_adc_remove() both called clk_disable_unprepare() unconditionally, causing the clk framework's enable/prepare counts to underflow on probe failure or module unbind. The issue went unnoticed because NPCM BMC firmware leaves the ADC clock enabled at boot, so the driver happened to work in practice. Switch to devm_clk_get_enabled() so the clock is properly enabled during probe and automatically released by the device-managed cleanup, and drop the now-redundant clk_disable_unprepare() from both the probe error path and remove(). While at it, drop the duplicate error message on devm_request_irq() failure since the IRQ core already logs it. Fixes: 9bf85fbc9d8f ("iio: adc: add NPCM ADC driver") Signed-off-by: David Carlier Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/npcm_adc.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index c8283873cdee..0418ecfbabaa 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -231,7 +231,7 @@ static int npcm_adc_probe(struct platform_device *pdev) if (IS_ERR(info->reset)) return PTR_ERR(info->reset); - info->adc_clk = devm_clk_get(&pdev->dev, NULL); + info->adc_clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(info->adc_clk)) { dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n"); return PTR_ERR(info->adc_clk); @@ -244,17 +244,13 @@ static int npcm_adc_probe(struct platform_device *pdev) info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); irq = platform_get_irq(pdev, 0); - if (irq < 0) { - ret = irq; - goto err_disable_clk; - } + if (irq < 0) + return irq; ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0, "NPCM_ADC", indio_dev); - if (ret < 0) { - dev_err(dev, "failed requesting interrupt\n"); - goto err_disable_clk; - } + if (ret < 0) + return ret; reg_con = ioread32(info->regs + NPCM_ADCCON); info->vref = devm_regulator_get_optional(&pdev->dev, "vref"); @@ -262,7 +258,7 @@ static int npcm_adc_probe(struct platform_device *pdev) ret = regulator_enable(info->vref); if (ret) { dev_err(&pdev->dev, "Can't enable ADC reference voltage\n"); - goto err_disable_clk; + return ret; } iowrite32(reg_con & ~NPCM_ADCCON_REFSEL, @@ -272,10 +268,8 @@ static int npcm_adc_probe(struct platform_device *pdev) * Any error which is not ENODEV indicates the regulator * has been specified and so is a failure case. */ - if (PTR_ERR(info->vref) != -ENODEV) { - ret = PTR_ERR(info->vref); - goto err_disable_clk; - } + if (PTR_ERR(info->vref) != -ENODEV) + return PTR_ERR(info->vref); /* Use internal reference */ iowrite32(reg_con | NPCM_ADCCON_REFSEL, @@ -314,8 +308,6 @@ static int npcm_adc_probe(struct platform_device *pdev) iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); -err_disable_clk: - clk_disable_unprepare(info->adc_clk); return ret; } @@ -332,7 +324,6 @@ static void npcm_adc_remove(struct platform_device *pdev) iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON); if (!IS_ERR(info->vref)) regulator_disable(info->vref); - clk_disable_unprepare(info->adc_clk); } static struct platform_driver npcm_adc_driver = { From 88c9dd5170e057f07ae1cbbf0b4c43ec050bd919 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Tue, 5 May 2026 12:34:32 +0800 Subject: [PATCH 532/697] iio: dac: ad3530r: Fix AD3531/AD3531R powerdown mode strings commit ebd250c2581ec46c64c73fdfa918c9a7f757505e upstream. The AD3531/AD3531R has different output operating modes from the AD3530/AD3530R. According to the AD3531/AD3531R datasheet, the powerdown modes are: 01: 500 Ohm output impedance 10: 3.85 kOhm output impedance 11: 16 kOhm output impedance The driver currently uses the AD3530R modes (1k, 7.7k, 32k) for all variants, which is incorrect for AD3531/AD3531R. Add AD3531R-specific powerdown mode strings and assign them to the AD3531/AD3531R chip variants. Fixes: 93583174a3df ("iio: dac: ad3530r: Add driver for AD3530R and AD3531R") Signed-off-by: Kim Seer Paller Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad3530r.c | 54 +++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/iio/dac/ad3530r.c b/drivers/iio/dac/ad3530r.c index 6134613777b8..a0625fc7ad48 100644 --- a/drivers/iio/dac/ad3530r.c +++ b/drivers/iio/dac/ad3530r.c @@ -108,6 +108,12 @@ static const char * const ad3530r_powerdown_modes[] = { "32kohm_to_gnd", }; +static const char * const ad3531r_powerdown_modes[] = { + "500ohm_to_gnd", + "3.85kohm_to_gnd", + "16kohm_to_gnd", +}; + static int ad3530r_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -136,6 +142,13 @@ static const struct iio_enum ad3530r_powerdown_mode_enum = { .set = ad3530r_set_powerdown_mode, }; +static const struct iio_enum ad3531r_powerdown_mode_enum = { + .items = ad3531r_powerdown_modes, + .num_items = ARRAY_SIZE(ad3531r_powerdown_modes), + .get = ad3530r_get_powerdown_mode, + .set = ad3530r_set_powerdown_mode, +}; + static ssize_t ad3530r_get_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, @@ -279,7 +292,20 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { { } }; -#define AD3530R_CHAN(_chan) \ +static const struct iio_chan_spec_ext_info ad3531r_ext_info[] = { + { + .name = "powerdown", + .shared = IIO_SEPARATE, + .read = ad3530r_get_dac_powerdown, + .write = ad3530r_set_dac_powerdown, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad3531r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &ad3531r_powerdown_mode_enum), + { } +}; + +#define AD3530R_CHAN(_chan, _ext_info) \ { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ @@ -287,25 +313,25 @@ static const struct iio_chan_spec_ext_info ad3530r_ext_info[] = { .output = 1, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ - .ext_info = ad3530r_ext_info, \ + .ext_info = _ext_info, \ } static const struct iio_chan_spec ad3530r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), - AD3530R_CHAN(4), - AD3530R_CHAN(5), - AD3530R_CHAN(6), - AD3530R_CHAN(7), + AD3530R_CHAN(0, ad3530r_ext_info), + AD3530R_CHAN(1, ad3530r_ext_info), + AD3530R_CHAN(2, ad3530r_ext_info), + AD3530R_CHAN(3, ad3530r_ext_info), + AD3530R_CHAN(4, ad3530r_ext_info), + AD3530R_CHAN(5, ad3530r_ext_info), + AD3530R_CHAN(6, ad3530r_ext_info), + AD3530R_CHAN(7, ad3530r_ext_info), }; static const struct iio_chan_spec ad3531r_channels[] = { - AD3530R_CHAN(0), - AD3530R_CHAN(1), - AD3530R_CHAN(2), - AD3530R_CHAN(3), + AD3530R_CHAN(0, ad3531r_ext_info), + AD3530R_CHAN(1, ad3531r_ext_info), + AD3530R_CHAN(2, ad3531r_ext_info), + AD3530R_CHAN(3, ad3531r_ext_info), }; static const struct ad3530r_chip_info ad3530_chip = { From 684bfd655b8084e744aa1e58cb7d29026a8e5ea1 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Mon, 27 Apr 2026 22:33:19 +0100 Subject: [PATCH 533/697] iio: dac: max5821: fix return value check in powerdown sync commit d0a228d903425e653f18a4341e60c0538afb6d41 upstream. The function max5821_sync_powerdown_mode() returned the result of i2c_master_send() directly. If a partial transfer occurred, it would be incorrectly treated as a success by the caller. While the caller currently handles the positive return value of 2 as success, this patch refactors the function to return 0 on full success and -EIO on short writes. This ensures robust error handling for incomplete transfers and improves code maintainability by using sizeof(outbuf). Fixes: 472988972737 ("iio: add support of the max5821") Signed-off-by: Salah Triki Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/max5821.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index e7e29359f8fe..dd4e35460195 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -90,6 +90,7 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, const struct iio_chan_spec *chan) { u8 outbuf[2]; + int ret; outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE; @@ -103,7 +104,13 @@ static int max5821_sync_powerdown_mode(struct max5821_data *data, else outbuf[1] |= MAX5821_EXTENDED_POWER_UP; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + if (ret != sizeof(outbuf)) + return -EIO; + + return 0; } static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev, From 69f7d101976c617e1f406bc7da6eb4129739bf54 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 1 May 2026 10:14:54 +0100 Subject: [PATCH 534/697] iio: dac: ad5686: fix ref bit initialization for single-channel parts commit ecae2ae606d493cf11457946436335bd0e726663 upstream. The reference bit position was ignored when writing the register at the probe() function (!!val was used). When such bit is 1, internal voltage reference is disabled so that an external one can be used. For multi-channel devices, bit 0 of the Internal Reference Setup command behaves the same way, so AD5686_REF_BIT_MSK is created. The issue exists since support for single-channel devices were first introduced. Fixes: be1b24d24541 ("iio:dac:ad5686: Add AD5691R/AD5692R/AD5693/AD5693R support") Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad5686.c | 6 +++--- drivers/iio/dac/ad5686.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 4b18498aa074..b85d5c5a864b 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -509,7 +509,7 @@ int ad5686_probe(struct device *dev, break; case AD5686_REGMAP: cmd = AD5686_CMD_INTERNAL_REFER_SETUP; - ref_bit_msk = 0; + ref_bit_msk = AD5686_REF_BIT_MSK; break; case AD5693_REGMAP: cmd = AD5686_CMD_CONTROL_REG; @@ -520,9 +520,9 @@ int ad5686_probe(struct device *dev, return -EINVAL; } - val = (has_external_vref | ref_bit_msk); + val = has_external_vref ? ref_bit_msk : 0; - ret = st->write(st, cmd, 0, !!val); + ret = st->write(st, cmd, 0, val); if (ret) return ret; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index e7d36bae3e59..36e16c5c4581 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -46,6 +46,7 @@ #define AD5310_REF_BIT_MSK BIT(8) #define AD5683_REF_BIT_MSK BIT(12) +#define AD5686_REF_BIT_MSK BIT(0) #define AD5693_REF_BIT_MSK BIT(12) /** From f541c9a1eb89346cb27a47e47ce5c33c59ee4cfc Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Fri, 1 May 2026 10:14:55 +0100 Subject: [PATCH 535/697] iio: dac: ad5686: fix input raw value check commit d01220ee5e43c65a206df827b39bf5cf5f7b9dce upstream. Fix range check for input raw value, which is off by one, i.e., for a 10-bit DAC the max valid value is 1023, but 1 << 10 equals 1024, which passes the previous check, allowing an out-of-range write. The issue exists since the ad5686 driver was first introduced. Fixes: c2f37c8dcadc ("iio: dac: New driver for AD5686R, AD5685R, AD5684R Digital to analog converters") Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad5686.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index b85d5c5a864b..27878a6318ff 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -154,7 +154,7 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > (1 << chan->scan_type.realbits) || val < 0) + if (val >= (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; mutex_lock(&st->lock); From 31de336a2c0d02b080488bb47b088d4fc1a26614 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Tue, 5 May 2026 13:35:04 +0100 Subject: [PATCH 536/697] iio: dac: ad5686: acquire lock when doing powerdown control commit 5237c3175cae5ab05f18878cec3301a04403859e upstream. Protect access of pwr_down_mode and pwr_down_mask fields with existing mutex lock. Each channel exposes their own attributes for controlling powerdown modes and powerdown state. This fixes potential race conditions as those the write functions perform non-atomic read-modify-write operations to those pwr_down_* fields. This issue exists since the ad5686 driver was first introduced. Fixes: c2f37c8dcadc ("iio: dac: New driver for AD5686R, AD5685R, AD5684R Digital to analog converters") Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad5686.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 27878a6318ff..2e443fcfeb39 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -30,6 +30,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; } @@ -39,6 +41,8 @@ static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); @@ -57,6 +61,8 @@ static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); + guard(mutex)(&st->lock); + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3 << (chan->channel * 2)))); } @@ -77,6 +83,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; + guard(mutex)(&st->lock); + if (readin) st->pwr_down_mask |= (0x3 << (chan->channel * 2)); else From 991d359faa9526963ae4dc6d16dd131d0894b4e6 Mon Sep 17 00:00:00 2001 From: Rodrigo Alencar Date: Tue, 5 May 2026 13:35:05 +0100 Subject: [PATCH 537/697] iio: dac: ad5686: fix powerdown control on dual-channel devices commit 8aeaf25a85263a7a43357e16ad78ab969f6f8aeb upstream. Fix powerdown control by using a proper bit shift for the powerdown mask values. During initialization, powerdown bits are initialized so that unused bits are set to 1 and the correct bit shift is used. Dual-channel devices use one-hot encoding in the address and that reflects on the position of the powerdown bits, which are not channel-index based for that case. Quad-channel devices also use one-hot encoding for the channel address but the result of log2(address) coincides with the channel index value. Mask as 0x3U is used rather than 0x3, because shift can reach value of 30 (last channel of a 16-channel device), which would mess with the sign bit. The issue was introduced when first adding support for dual-channel devices, which overlooked powerdown control differences. Fixes: 7dc8faeab3e3 ("iio: dac: ad5686: add support for AD5338R") Signed-off-by: Rodrigo Alencar Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/dac/ad5686.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 2e443fcfeb39..a7213bc6b156 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -25,26 +25,37 @@ static const char * const ad5686_powerdown_modes[] = { "three_state" }; +static inline unsigned int ad5686_pd_mask_shift(const struct iio_chan_spec *chan) +{ + if (chan->channel == chan->address) + return chan->channel * 2; + + /* one-hot encoding is used in dual/quad channel devices */ + return __ffs(chan->address) * 2; +} + static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1; + return ((st->pwr_down_mode >> shift) & 0x3U) - 1; } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int mode) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - st->pwr_down_mode &= ~(0x3 << (chan->channel * 2)); - st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2)); + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= (mode + 1) << shift; return 0; } @@ -59,12 +70,12 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { + unsigned int shift = ad5686_pd_mask_shift(chan); struct ad5686_state *st = iio_priv(indio_dev); guard(mutex)(&st->lock); - return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3U << shift))); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, @@ -86,9 +97,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, guard(mutex)(&st->lock); if (readin) - st->pwr_down_mask |= (0x3 << (chan->channel * 2)); + st->pwr_down_mask |= 0x3U << ad5686_pd_mask_shift(chan); else - st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); + st->pwr_down_mask &= ~(0x3U << ad5686_pd_mask_shift(chan)); switch (st->chip_info->regmap_type) { case AD5310_REGMAP: @@ -468,7 +479,7 @@ int ad5686_probe(struct device *dev, { struct ad5686_state *st; struct iio_dev *indio_dev; - unsigned int val, ref_bit_msk; + unsigned int val, ref_bit_msk, shift; bool has_external_vref; u8 cmd; int ret, i; @@ -492,9 +503,18 @@ int ad5686_probe(struct device *dev, has_external_vref = ret != -ENODEV; st->vref_mv = has_external_vref ? ret / 1000 : st->chip_info->int_vref_mv; + /* Initialize masks to all ones provided the max shift (last channel) */ + shift = ad5686_pd_mask_shift(&st->chip_info->channels[st->chip_info->num_channels - 1]); + st->pwr_down_mask = GENMASK(shift + 1, 0); + st->pwr_down_mode = GENMASK(shift + 1, 0); + /* Set all the power down mode for all channels to 1K pulldown */ - for (i = 0; i < st->chip_info->num_channels; i++) - st->pwr_down_mode |= (0x01 << (i * 2)); + for (i = 0; i < st->chip_info->num_channels; i++) { + shift = ad5686_pd_mask_shift(&st->chip_info->channels[i]); + st->pwr_down_mask &= ~(0x3U << shift); /* powered up state */ + st->pwr_down_mode &= ~(0x3U << shift); + st->pwr_down_mode |= 0x01U << shift; + } indio_dev->name = name; indio_dev->info = &ad5686_info; From 944082fdb0284a31c0b37a88c8a1d4404da3a6d9 Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Mon, 27 Apr 2026 21:12:38 +0100 Subject: [PATCH 538/697] iio: adc: mt6359: fix unchecked return value in mt6358_read_imp commit f9bbd943c34a9ad60e593a4b99ce2394e4e2381b upstream. In mt6358_read_imp(), the variable val_v is passed to regmap_read() but the return value is not checked. If the read fails, val_v remains uninitialized and its random stack content is subsequently reported as a measurement result. Initialize val_v to zero to ensure a predictable value is reported in case of bus failure and to prevent potential stack data leakage. This also satisfies static analyzers that might otherwise flag the variable as used uninitialized. Fixes: 3587914bf61d ("iio: adc: Add support for MediaTek MT6357/8/9 Auxiliary ADC") Signed-off-by: Salah Triki Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/mt6359-auxadc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/mt6359-auxadc.c b/drivers/iio/adc/mt6359-auxadc.c index f426a289e867..67ca23d37a21 100644 --- a/drivers/iio/adc/mt6359-auxadc.c +++ b/drivers/iio/adc/mt6359-auxadc.c @@ -497,6 +497,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, return ret; /* Read the params before stopping */ + val_v = 0; regmap_read(regmap, reg_adc0 + (cinfo->imp_adc_num << 1), &val_v); mt6358_stop_imp_conv(adc_dev); From 7155e7fce4290533843d6e3784fe8575d14a8aca Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Thu, 7 May 2026 20:07:51 +0100 Subject: [PATCH 539/697] iio: adc: viperboard: Fix error handling in vprbrd_iio_read_raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 422b5bbf333f75fb486855ad0eedc23cf21f3277 upstream. The driver proceeds to the reception phase even if the preceding transmission fails. This uses a goto error label for an early bail out and ensures the mutex is properly unlocked in case of failure. Fixes: ffd8a6e7a778 ("iio: adc: Add viperboard adc driver") Signed-off-by: Salah Triki Reviewed-by: Joshua Crofts Reviewed-by: Maxwell Doose Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/viperboard_adc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index 9bb0b83c8f67..6efe1c618ef7 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -70,8 +70,10 @@ static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); if (ret != sizeof(struct vprbrd_adc_msg)) { - dev_err(&iio_dev->dev, "usb send error on adc read\n"); + mutex_unlock(&vb->lock); error = -EREMOTEIO; + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + goto error; } ret = usb_control_msg(vb->usb_dev, From 5cb8cede8baf771bec4c033ac690049204c26ae1 Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Wed, 8 Apr 2026 13:32:13 +0300 Subject: [PATCH 540/697] iio: adc: ad4695: Fix call ordering in offload buffer postenable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1a772719318c11e146f6fbe621fffd230a6f456a upstream. ad4695_enter_advanced_sequencer_mode() was called after spi_offload_trigger_enable(). That is wrong because ad4695_enter_advanced_sequencer_mode() issues regular SPI transfers to put the ADC into advanced sequencer mode, and not all SPI offload capable controllers support regular SPI transfers while offloading is enabled. Fix this by calling ad4695_enter_advanced_sequencer_mode() before spi_offload_trigger_enable(), so the ADC is fully configured before the first CNV pulse can occur. This is consistent with the same constraint that already applies to the BUSY_GP_EN write above it. Update the error unwind labels accordingly: add err_exit_conversion_mode so that a failure of spi_offload_trigger_enable() correctly exits conversion mode before clearing BUSY_GP_EN. Fixes: f09f140e3ea8 ("iio: adc: ad4695: Add support for SPI offload") Reviewed-by: Nuno Sá Reviewed-by: David Lechner Signed-off-by: Radu Sabau Cc: Stable@vger.kernel.org Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/adc/ad4695.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/ad4695.c b/drivers/iio/adc/ad4695.c index cda419638d9a..53642de7330d 100644 --- a/drivers/iio/adc/ad4695.c +++ b/drivers/iio/adc/ad4695.c @@ -876,14 +876,14 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unoptimize_message; - ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, - &config); + ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); if (ret) goto err_disable_busy_output; - ret = ad4695_enter_advanced_sequencer_mode(st, num_slots); + ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, + &config); if (ret) - goto err_offload_trigger_disable; + goto err_exit_conversion_mode; mutex_lock(&st->cnv_pwm_lock); pwm_get_state(st->cnv_pwm, &state); @@ -895,23 +895,16 @@ static int ad4695_offload_buffer_postenable(struct iio_dev *indio_dev) ret = pwm_apply_might_sleep(st->cnv_pwm, &state); mutex_unlock(&st->cnv_pwm_lock); if (ret) - goto err_offload_exit_conversion_mode; + goto err_offload_trigger_disable; return 0; -err_offload_exit_conversion_mode: - /* - * We have to unwind in a different order to avoid triggering offload. - * ad4695_exit_conversion_mode() triggers a conversion, so it has to be - * done after spi_offload_trigger_disable(). - */ - spi_offload_trigger_disable(st->offload, st->offload_trigger); - ad4695_exit_conversion_mode(st); - goto err_disable_busy_output; - err_offload_trigger_disable: spi_offload_trigger_disable(st->offload, st->offload_trigger); +err_exit_conversion_mode: + ad4695_exit_conversion_mode(st); + err_disable_busy_output: regmap_clear_bits(st->regmap, AD4695_REG_GP_MODE, AD4695_REG_GP_MODE_BUSY_GP_EN); From 15a0b3f33ffb6c78b3de6f69b026ceb09b973dd1 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 5 May 2026 14:37:48 +0100 Subject: [PATCH 541/697] iio: gyro: itg3200: fix i2c read into the wrong stack location commit 6bdc3023d62ed5c7d591f0eb27a5adb37fb892ae upstream. itg3200_read_all_channels() takes `__be16 *buf' as a parameter and fills the i2c_msg destination as `(char *)&buf'. Since `buf' is the parameter (a pointer), `&buf' is the address of the local pointer slot on the stack of itg3200_read_all_channels(), not the address of the caller's scan buffer. The (char *) cast hides the type mismatch. i2c_transfer() therefore writes ITG3200_SCAN_ELEMENTS * sizeof(s16) = 8 bytes into the parameter's stack slot, which is discarded when the function returns. The caller's scan buffer in itg3200_trigger_handler() is never written to, so iio_push_to_buffers_with_timestamp() pushes uninitialised stack contents to userspace via /dev/iio:deviceX every scan -- both a functional bug (no actual gyroscope or temperature data is delivered through the triggered buffer) and an information leak. The non-buffered read_raw() path is unaffected: it goes through itg3200_read_reg_s16() which uses `&out' on a local s16 value, where that is correct. Drop the spurious `&' so the i2c read writes into the caller's buffer. Fixes: 9dbf091da080 ("iio: gyro: Add itg3200") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/itg3200_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index cf97adfa9727..87efa2c74ca4 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -34,7 +34,7 @@ static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf) .addr = i2c->addr, .flags = i2c->flags | I2C_M_RD, .len = ITG3200_SCAN_ELEMENTS * sizeof(s16), - .buf = (char *)&buf, + .buf = (char *)buf, }, }; From aaf9d640e9ae1172d0a9c659ecb245a50a10850a Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 31 Mar 2026 13:13:00 +0300 Subject: [PATCH 542/697] iio: gyro: adis16260: fix division by zero in write_raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 761e8b489e6cf166c574034b70637f8a7eadd0ee upstream. Add a validation check for the sampling frequency value before using it as a divisor. A user writing zero to the sampling_frequency sysfs attribute triggers a division by zero in the kernel. Fixes: 089a41985c6c ("staging: iio: adis16260 digital gyro driver") Signed-off-by: Antoniu Miclaus Reviewed-by: Nuno Sá Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/gyro/adis16260.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 586e6cfa14a9..91b9c5f18ec4 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -287,6 +287,9 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: + if (val <= 0) + return -EINVAL; + if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else From 04a4d9822210937707ca61980f82d9de4f8ab5f0 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Sun, 26 Apr 2026 14:47:04 +0530 Subject: [PATCH 543/697] iio: ssp_sensors: cancel delayed work_refresh on remove commit eedf7602fbd929e97e0c480da501dc7a34beb2a8 upstream. The work_refresh may still be pending or running when the device is removed, cancel the delayed work_refresh in remove path. Fixes: 50dd64d57eee ("iio: common: ssp_sensors: Add sensorhub driver") Signed-off-by: Sanjay Chitroda Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/common/ssp_sensors/ssp_dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index da09c9f3ceb6..e2538a84c812 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -590,6 +590,7 @@ static void ssp_remove(struct spi_device *spi) ssp_clean_pending_list(data); free_irq(data->spi->irq, data); + cancel_delayed_work_sync(&data->work_refresh); timer_delete_sync(&data->wdt_timer); cancel_work_sync(&data->work_wdt); From aefc19ca3dd3db67e2dd40a194e39854689d80fe Mon Sep 17 00:00:00 2001 From: Salah Triki Date: Tue, 5 May 2026 08:10:24 +0100 Subject: [PATCH 544/697] iio: temperature: tsys01: fix broken PROM checksum validation commit 4701e471c16866e7aa8f5e6a3a6b0d31e097e2c9 upstream. The current implementation of tsys01_crc_valid() incorrectly sums the first word (n_prom[0]) repeatedly instead of iterating over the 8 words retrieved from the PROM. This leads to a checksum mismatch and probe failure on hardware. According to the TSYS01 datasheet, the PROM consists of 8 words. A valid check must iterate through all 8 words to verify the integrity of the calibration data. The current driver only checks the first word 8 times. Note: This fix was identified during a code audit and is based on datasheet specifications. It has not been tested on real hardware. Fixes: 43e53407f680 ("Add tsys01 meas-spec driver support") Signed-off-by: Salah Triki Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/temperature/tsys01.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 334bba6fdae6..104dd45598b0 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -119,7 +119,7 @@ static bool tsys01_crc_valid(u16 *n_prom) u8 sum = 0; for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++) - sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF)); + sum += ((n_prom[cnt] >> 8) + (n_prom[cnt] & 0xFF)); return (sum == 0); } From ae01ec83841d7dcc716156d06631a514af68126b Mon Sep 17 00:00:00 2001 From: Advait Dhamorikar Date: Tue, 7 Apr 2026 12:50:59 +0530 Subject: [PATCH 545/697] iio: magnetometer: st_magn: fix default DRDY pin selection for LIS2MDL commit 49f79cd28f1e3333cbe0d616ce59ead0b24bf34e upstream. The device tree binding for st,lis2mdl does not support st,drdy-int-pin property. However, when no platform data is provided and the property is absent, the driver falls back to default_magn_pdata which hardcodes drdy_int_pin = 2. This causes `st_sensors_set_drdy_int_pin` to fail with -EINVAL because the LIS2MDL sensor settings have no INT2 DRDY mask defined. Fix this by checking the sensor's INT2 DRDY mask availability at probe time and selecting the appropriate default pin. Sensors that do not support INT2 DRDY will default to INT1, while all others retain the existing default of INT2. Fixes: 38934daf7b5c ("iio: magnetometer: st_magn: Provide default platform data") Signed-off-by: Advait Dhamorikar Reviewed-by: Andy Shevchenko Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/magnetometer/st_magn_core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index ef348d316c00..7644bd04654b 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -506,6 +506,11 @@ static const struct st_sensors_platform_data default_magn_pdata = { .drdy_int_pin = 2, }; +/* LIS2MDL only supports DRDY on INT1 */ +static const struct st_sensors_platform_data alt_magn_pdata = { + .drdy_int_pin = 1, +}; + static int st_magn_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -628,8 +633,12 @@ int st_magn_common_probe(struct iio_dev *indio_dev) mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0]; mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz; - if (!pdata) - pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + if (!pdata) { + if (mdata->sensor_settings->drdy_irq.int2.mask) + pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + else + pdata = (struct st_sensors_platform_data *)&alt_magn_pdata; + } err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) From 8d4daa6144404ea938935a9f1989550b5f7b7d2a Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Fri, 27 Mar 2026 20:27:54 +0800 Subject: [PATCH 546/697] iio: light: veml6070: Fix resource leak in probe error path commit b66f922f6a4fa92840f662fbcfeb4f8a0f774bcc upstream. The driver calls i2c_new_dummy_device() to create a dummy device, then calls i2c_smbus_write_byte(). If i2c_smbus_write_byte() fails and returns, the cleanup via devm_add_action_or_reset() was never registered, so the dummy device leaks. Switch to devm_i2c_new_dummy_device() which registers cleanup atomically with device creation, eliminating the error-path window. Fixes: 7501bff87c3e ("iio: light: veml6070: add action for i2c_unregister_device") Reviewed-by: Andy Shevchenko Signed-off-by: Felix Gu Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/veml6070.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/iio/light/veml6070.c b/drivers/iio/light/veml6070.c index 74d7246e5225..4bbd86d0cb46 100644 --- a/drivers/iio/light/veml6070.c +++ b/drivers/iio/light/veml6070.c @@ -245,13 +245,6 @@ static const struct iio_info veml6070_info = { .write_raw = veml6070_write_raw, }; -static void veml6070_i2c_unreg(void *p) -{ - struct veml6070_data *data = p; - - i2c_unregister_device(data->client2); -} - static int veml6070_probe(struct i2c_client *client) { struct veml6070_data *data; @@ -281,7 +274,8 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB); + data->client2 = devm_i2c_new_dummy_device(&client->dev, client->adapter, + VEML6070_ADDR_DATA_LSB); if (IS_ERR(data->client2)) return dev_err_probe(&client->dev, PTR_ERR(data->client2), "i2c device for second chip address failed\n"); @@ -292,10 +286,6 @@ static int veml6070_probe(struct i2c_client *client) if (ret < 0) return ret; - ret = devm_add_action_or_reset(&client->dev, veml6070_i2c_unreg, data); - if (ret < 0) - return ret; - return devm_iio_device_register(&client->dev, indio_dev); } From cbd2d7e6bd4f4828acbe784f33453baba911cd06 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 16 Apr 2026 14:14:42 +0300 Subject: [PATCH 547/697] iio: Fix iio_multiply_value use in iio_read_channel_processed_scale commit bb21ee31f5753a7972148798fd7dfb841dd33bdb upstream. The function iio_multiply_value returns IIO_VAL_INT (1) on success or a negative error number on failure, while iio_read_channel_processed_scale should return an error code or 0. This creates a situation where the expected result is treated as an error. Fix this by checking the iio_multiply_value result separately, instead of passing it as a return value. Fixes: 05f958d003c9 ("iio: Improve iio_read_channel_processed_scale() precision") Signed-off-by: Svyatoslav Ryhel Reviewed-by: Hans de Goede Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/inkern.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 1e5eb5a41271..ce07a401c012 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -738,7 +738,11 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, if (ret < 0) return ret; - return iio_multiply_value(val, scale, ret, pval, pval2); + ret = iio_multiply_value(val, scale, ret, pval, pval2); + if (ret < 0) + return ret; + + return 0; } else { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); if (ret < 0) From a5a05410cb34bfa486d63684cdc1f87a3b13f20a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 2 Apr 2026 13:40:15 +0800 Subject: [PATCH 548/697] iio: chemical: mhz19b: reject oversized serial replies commit 673478bc29cf72010faaf293c1c8c667393335a0 upstream. mhz19b_receive_buf() appends each serdev chunk into the fixed MHZ19B_CMD_SIZE receive buffer and advances buf_idx by len without checking that the chunk fits in the remaining space. A large callback can therefore overflow st->buf before the command path validates the reply. Reset the reply state before each command and reject oversized serial replies before copying them into the fixed buffer. When an oversized reply is detected, wake the waiter and report -EMSGSIZE instead of overwriting st->buf. Fixes: 4572a70b3681 ("iio: chemical: Add support for Winsen MHZ19B CO2 sensor") Cc: stable@vger.kernel.org Signed-off-by: Pengpeng Hou Acked-by: Gyeyoung Baek Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/chemical/mhz19b.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/iio/chemical/mhz19b.c b/drivers/iio/chemical/mhz19b.c index 3c64154918b1..9d4cf432919e 100644 --- a/drivers/iio/chemical/mhz19b.c +++ b/drivers/iio/chemical/mhz19b.c @@ -52,6 +52,8 @@ struct mhz19b_state { struct completion buf_ready; u8 buf_idx; + bool buf_overflow; + /* * Serdev receive buffer. * When data is received from the MH-Z19B, @@ -106,6 +108,10 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) cmd_buf[8] = mhz19b_get_checksum(cmd_buf); /* Write buf to uart ctrl synchronously */ + st->buf_idx = 0; + st->buf_overflow = false; + reinit_completion(&st->buf_ready); + ret = serdev_device_write(serdev, cmd_buf, MHZ19B_CMD_SIZE, 0); if (ret < 0) return ret; @@ -121,6 +127,9 @@ static int mhz19b_serdev_cmd(struct iio_dev *indio_dev, int cmd, u16 arg) if (!ret) return -ETIMEDOUT; + if (st->buf_overflow) + return -EMSGSIZE; + if (st->buf[8] != mhz19b_get_checksum(st->buf)) { dev_err(dev, "checksum err"); return -EINVAL; @@ -240,6 +249,14 @@ static size_t mhz19b_receive_buf(struct serdev_device *serdev, { struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); struct mhz19b_state *st = iio_priv(indio_dev); + size_t remaining = MHZ19B_CMD_SIZE - st->buf_idx; + + if (len > remaining) { + st->buf_idx = 0; + st->buf_overflow = true; + complete(&st->buf_ready); + return len; + } memcpy(st->buf + st->buf_idx, data, len); st->buf_idx += len; From 5e4d34092a5ebfbc3a45a180c76ecb1cdbbedd53 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Wed, 1 Apr 2026 14:08:29 +0300 Subject: [PATCH 549/697] iio: chemical: scd30: fix division by zero in write_raw commit 5aba4f94b225617a55fed442a70329b2ee19c0a5 upstream. Add a zero check for val2 before using it as a divisor when setting the sampling frequency. A user writing a zero fractional part to the sampling_frequency sysfs attribute triggers a division by zero in the kernel. Fixes: 64b3d8b1b0f5 ("iio: chemical: scd30: add core driver") Signed-off-by: Antoniu Miclaus Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/chemical/scd30_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index a665fcb78806..11d6bc1b63e6 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -256,7 +256,7 @@ static int scd30_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const guard(mutex)(&state->lock); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val) + if (val || !val2) return -EINVAL; val = 1000000000 / val2; From 390254cf509b8e433b17287bb3aa1990c942d3af Mon Sep 17 00:00:00 2001 From: Aldo Conte Date: Tue, 7 Apr 2026 17:17:01 +0200 Subject: [PATCH 550/697] iio: light: cm3323: fix reg_conf not being initialized correctly commit 1f4f0bcc5255dec5c4c3a1551bf49d8c33b69b20 upstream. The code stores the return value of i2c_smbus_write_word_data() in data->reg_conf; however, this value represents the result of the write operation and not the value actually written to the configuration register. This meant that the contents of data->reg_conf did not truly reflect the contents of the hardware register. Instead, save the value of the register before the write and use this value in the I2C write. The bug was found by code inspection: i2c_smbus_write_word_data() returns 0 on success, not the value written to the register. Tested using i2c-stub on a Raspberry Pi 3B running a custom 6.19.10 kernel. Before loading the driver, the configuration register 0x00 CM3323_CMD_CONF was populated with 0x0030 using `i2cset -y 11 0x10 0x00 0x0030 w`, encoding an integration time of 320ms in bits[6:4]. Due to incorrect initialization of data->reg_conf in cm3323_init(), the print of integration_time returns 0.040000 instead of the expected 0.320000. This happens because the read of the integration_time depends on cm3323_get_it_bits() that is based on the value of data->reg_conf, which is erroneously set to 0. With this fix applied, data->reg_conf correctly saves 0x0030 after init and the successive integration_time reports 0.320000 as expected. Fixes: 8b0544263761 ("iio: light: Add support for Capella CM3323 color sensor") Cc: stable@vger.kernel.org Signed-off-by: Aldo Conte Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/light/cm3323.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c index 79ad6e2209ca..0fe61b8a7029 100644 --- a/drivers/iio/light/cm3323.c +++ b/drivers/iio/light/cm3323.c @@ -89,15 +89,14 @@ static int cm3323_init(struct iio_dev *indio_dev) /* enable sensor and set auto force mode */ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); + data->reg_conf = ret; - ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret); + ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, data->reg_conf); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_conf\n"); return ret; } - data->reg_conf = ret; - return 0; } From a3763ae33476328cf8d661742deb9daec78eac96 Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Thu, 30 Apr 2026 21:29:06 +0800 Subject: [PATCH 551/697] iio: buffer: hw-consumer: fix use-after-free in error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6f5ed4f2c7c83f33344e0ba179f72a12e5dad4a4 upstream. In the err_put_buffers cleanup path of iio_hw_consumer_alloc(), the code was using list_for_each_entry() to iterate through buffers while calling iio_buffer_put() which can free the current buffer if refcount drops to 0. The list_for_each_entry() loop macro then evaluates buf->head.next to continue iteration, accessing the freed buffer. Fix this by using list_for_each_entry_safe(). Fixes: 48b66f8f936f ("iio: Add hardware consumer buffer support") Reported-by: sashiko Closes: https://sashiko.dev/#/patchset/20260427-iio_buf-v1-1-2bbdac844647%40gmail.com Signed-off-by: Felix Gu Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sá Reviewed-by: Maxwell Doose Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/buffer/industrialio-hw-consumer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index 526b2a8d725d..534252db48cf 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -82,7 +82,7 @@ static struct hw_consumer_buffer *iio_hw_consumer_get_buffer( */ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) { - struct hw_consumer_buffer *buf; + struct hw_consumer_buffer *buf, *tmp; struct iio_hw_consumer *hwc; struct iio_channel *chan; int ret; @@ -113,7 +113,7 @@ struct iio_hw_consumer *iio_hw_consumer_alloc(struct device *dev) return hwc; err_put_buffers: - list_for_each_entry(buf, &hwc->buffers, head) + list_for_each_entry_safe(buf, tmp, &hwc->buffers, head) iio_buffer_put(&buf->buffer); iio_channel_release_all(hwc->channels); err_free_hwc: From 3412a95afaa5d3262008dfc34f3c7be33d8151dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Monin?= Date: Wed, 1 Apr 2026 17:24:58 +0200 Subject: [PATCH 552/697] iio: buffer: Fix DMA fence leak in iio_buffer_enqueue_dmabuf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a093999355084bdbfe6e97f1dd232e58a1525f0b upstream. iio_buffer_enqueue_dmabuf() allocates a struct iio_dma_fence (104 bytes, kmalloc-128) via kmalloc_obj()+dma_fence_init(), which sets the initial kref to 1. It then calls dma_resv_add_fence() which takes a second reference (kref=2), and stores a raw pointer in block->fence. On the success path the function returns without calling dma_fence_put() to release the initial reference, so every buffer enqueue permanently leaks one kmalloc-128 allocation. The iio_buffer_cleanup() work item only releases the temporary reference taken during completion signalling by iio_buffer_signal_dmabuf_done(); the initial reference from dma_fence_init() is never released. With four iio_rwdev instances at 240kHz and 512 samples per buffer, this produces ~1875 kmalloc-128 allocations per second matching the observed slab growth exactly. A test with ftrace confirmed that the dma_fence_destroy event was never triggered. Fix by calling dma_fence_put() after dma_resv_add_fence(), transferring ownership of the fence to the DMA reservation object. The DMA fence then gets properly discarded after being signalled. Fixes: 3e26d9f08fbe0 ("iio: core: Add new DMABUF interface infrastructure") Originally-by: James Nuss Signed-off-by: Benoît Monin Reviewed-by: Paul Cercueil Cc: Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/iio/industrialio-buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index c46a5f37ccc9..69191aaaf808 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1911,6 +1911,7 @@ static int iio_buffer_enqueue_dmabuf(struct iio_dev_buffer_pair *ib, dma_resv_add_fence(dmabuf->resv, &fence->base, dma_to_ram ? DMA_RESV_USAGE_WRITE : DMA_RESV_USAGE_READ); + dma_fence_put(&fence->base); dma_resv_unlock(dmabuf->resv); cookie = dma_fence_begin_signalling(); From 0fee0ccac29e088d4bfab7e2d075725dcecd803d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:20:58 +0200 Subject: [PATCH 553/697] USB: serial: omninet: fix memory corruption with small endpoint commit 60df93d30f9bdd27db17c4d80ed80ef718d7226b upstream. Make sure that the bulk-out buffers are at least as large as the hardcoded transfer size to avoid user-controlled slab corruption should a malicious device report a smaller endpoint max packet size than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/omninet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 397ebd5a3e74..91cefff72246 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -30,6 +30,10 @@ /* This one seems to be a re-branded ZyXEL device */ #define BT_IGNITIONPRO_ID 0x2000 +#define OMNINET_HEADERLEN 4 +#define OMNINET_BULKOUTSIZE 64 +#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) + /* function prototypes */ static void omninet_process_read_urb(struct urb *urb); static int omninet_prepare_write_buffer(struct usb_serial_port *port, @@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net usb", .id_table = id_table, .num_bulk_out = 2, + .bulk_out_size = OMNINET_BULKOUTSIZE, .calc_num_ports = omninet_calc_num_ports, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, @@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port) kfree(od); } -#define OMNINET_HEADERLEN 4 -#define OMNINET_BULKOUTSIZE 64 -#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) - static void omninet_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; From b2723bd468c5ce2ce4e5ee9d41dd21e6b6009f47 Mon Sep 17 00:00:00 2001 From: Yongchao Wu Date: Thu, 14 May 2026 00:00:12 +0800 Subject: [PATCH 554/697] usb: cdns3: gadget: fix request skipping after clearing halt commit c8778ff817a7047d6848fefba99dcb27b1bf01fe upstream. According to the cdns3 datasheet, the EPRST (Endpoint Reset) command causes the DMA engine to reposition its internal pointer to the next Transfer Descriptor (TD) if it was already processing one. This issue is consistently observed during the ADB identification process on macOS hosts, where the host issues a Clear_Halt. Although commit 4bf2dd65135a ("usb: cdns3: gadget: toggle cycle bit before reset endpoint") attempted to avoid DMA advance by toggling the cycle bit, trace logs show that on certain hosts like macOS, the DMA pointer (EP_TRADDR) still shifts after EPRST: cdns3_ctrl_req: Clear Endpoint Feature(Halt ep1out) cdns3_doorbell_epx: ep1out, ep_trbaddr f9c04030 <-- Should be f9c04000 cdns3_gadget_giveback: ep1out: req: ... length: 16384/16384 As shown above, the DMA pointer jumped to the next TD, causing the controller to skip the initial TRBs of the request. This leads to data misalignment and ADB protocol hangs on macOS. Fix this by manually restoring the EP_TRADDR register to the starting physical address of the current request after the EPRST operation is complete. Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver") Cc: stable Cc: Peter Chen Signed-off-by: Yongchao Wu Acked-by: Peter Chen Link: https://patch.msgid.link/20260513160012.2547894-1-yongchao.wu@autochips.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 6b8b0354b41a..bc1157a4df00 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); if (request) { - if (trb) + if (trb) { *trb = trb_tmp; + /* + * Per datasheet, EPRST causes DMA to reposition to the next TD. + * Manually reset EP_TRADDR to the current TRB to prevent + * the hardware from skipping the interrupted request. + */ + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma + + priv_req->start_trb * TRB_SIZE), + &priv_dev->regs->ep_traddr); + } + cdns3_rearm_transfer(priv_ep, 1); } From 459c4fa089f79a68b8217707cb21415317f4a83f Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 13 May 2026 16:53:09 +0800 Subject: [PATCH 555/697] usb: cdns3: plat: fix leaked usb2_phy initialization on usb3_phy acquisition failure commit e6970cda63fd4b4546aeed9d0e2f53a7c95cd09c upstream. Move usb2_phy initialization after usb3_phy acquisition. Fixes: f738957277ba ("usb: cdns3: Split core.c into cdns3-plat and core.c file") Cc: stable Reported-by: sashiko-bot Closes: https://lore.kernel.org/linux-devicetree/agKaEePSFknhDBg2@nchen-desktop/T/#m21e1d9c1574eb127ce03c0c2a1a49002ce435b52 Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260513085310.2217547-2-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 735df88774e4..d2e8d1e9007b 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), "Failed to get cdn3,usb2-phy\n"); - ret = phy_init(cdns->usb2_phy); - if (ret) - return ret; - cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), "Failed to get cdn3,usb3-phy\n"); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + ret = phy_init(cdns->usb3_phy); if (ret) goto err_phy3_init; From 94c92f9c886ca0a8b74207e9994f39e47242dd1b Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Wed, 13 May 2026 16:53:10 +0800 Subject: [PATCH 556/697] usb: cdns3: plat: fix unbalanced pm_runtime_forbid() call permanently leaks the runtime PM usage counter across bind/unbind cycles commit ae6f3b82324e4f39ad8443c9020787e6fc889637 upstream. Call pm_runtime_allow(dev) conditionally at cdns3_plat_remove. Fixes: f738957277ba ("usb: cdns3: Split core.c into cdns3-plat and core.c file") Cc: stable Reported-by: sashiko-bot Closes: https://lore.kernel.org/linux-devicetree/agKaEePSFknhDBg2@nchen-desktop/T/#m21e1d9c1574eb127ce03c0c2a1a49002ce435b52 Signed-off-by: Peter Chen Link: https://patch.msgid.link/20260513085310.2217547-3-peter.chen@cixtech.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index d2e8d1e9007b..94e9706a1806 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -186,6 +186,9 @@ static void cdns3_plat_remove(struct platform_device *pdev) struct device *dev = cdns->dev; pm_runtime_get_sync(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_allow(dev); + pm_runtime_disable(dev); pm_runtime_put_noidle(dev); cdns_remove(cdns); From 84ea928ed584756e59c6ac09736f12d1db95ded0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 20 May 2026 08:59:28 +0300 Subject: [PATCH 557/697] usb: dwc2: Fix use after free in debug code commit 9ea06a3fbf9f16e0d98c52cb3b99642be15ec281 upstream. We're not allowed to dereference "urb" after calling usb_hcd_giveback_urb() so save the urb->status ahead of time. Fixes: 7359d482eb4d ("staging: HCD files for the DWC2 driver") Cc: stable Signed-off-by: Dan Carpenter Link: https://patch.msgid.link/ag1NwBpqT4IEQcdJ@stanley.mountain Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 30eb8506617c..e34aaa59a3d8 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); int rc; unsigned long flags; + int urb_status; dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n"); dwc2_dump_urb_info(hcd, urb, "urb_dequeue"); @@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, /* Higher layer software sets URB status */ spin_unlock(&hsotg->lock); + urb_status = urb->status; usb_hcd_giveback_urb(hcd, urb, status); spin_lock(&hsotg->lock); dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n"); - dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status); + dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status); out: spin_unlock_irqrestore(&hsotg->lock, flags); From bf769358419e00344c1b16fa034d058f563d46a1 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 25 Apr 2026 22:07:06 -0700 Subject: [PATCH 558/697] Input: elan_i2c - validate firmware size before use commit 76b0d0baa9ae9c60e726bbe1b6ff0bec2c993634 upstream. Ensure that the firmware file is large enough to contain the expected number of pages and the signature (which resides at the end of the firmware blob) before accessing them to prevent potential out-of-bounds reads. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/ae2dOgiFvXRm4BHo@google.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/elan_i2c_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index fee1796da3d0..74f822cd8774 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -645,6 +645,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, return error; } + if (fw->size < data->fw_signature_address + sizeof(signature)) { + dev_err(dev, "firmware file too small\n"); + return -EBADF; + } + /* Firmware file must match signature data */ fw_signature = &fw->data[data->fw_signature_address]; if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { From 3f43865cb64dd7cb50efae1281a95585617b12a1 Mon Sep 17 00:00:00 2001 From: Chaitanya Sabnis Date: Tue, 26 May 2026 15:52:40 +0530 Subject: [PATCH 559/697] i2c: davinci: fix division by zero on missing clock-frequency commit 030675aa54cf757769b3db65642433d626b3ed7c upstream. When the 'clock-frequency' property is missing from the device tree, the driver falls back to DAVINCI_I2C_DEFAULT_BUS_FREQ. However, this macro was defined in kHz (100), whereas the device tree property is expected in Hz. The probe function divided the fallback value by 1000, causing integer truncation that resulted in dev->bus_freq = 0. This triggered a deterministic division-by-zero kernel panic when calculating clock dividers later in the probe sequence. Fix this by redefining DAVINCI_I2C_DEFAULT_BUS_FREQ in Hz (100000) to match the expected device tree property unit, allowing the existing division logic to work correctly for both cases. Fixes: b04ce6385979 ("i2c: davinci: kill platform data") Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260514044726.57297C2BCB7@smtp.kernel.org/ Signed-off-by: Chaitanya Sabnis Cc: # v6.14+ Reviewed-by: Bartosz Golaszewski Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260526102240.4949-1-chaitanya.msabnis@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index a773ba082321..66c23535656b 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -117,7 +117,7 @@ /* timeout for pm runtime autosuspend */ #define DAVINCI_I2C_PM_TIMEOUT 1000 /* ms */ -#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100 +#define DAVINCI_I2C_DEFAULT_BUS_FREQ 100000 struct davinci_i2c_dev { struct device *dev; From d59cc66b702757e3c5a711e78a38583eac0c2738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Lothor=C3=A9=20=28eBPF=20Foundation=29?= Date: Wed, 27 May 2026 21:12:31 +0200 Subject: [PATCH 560/697] x86/ftrace: Relocate %rip-relative percpu refs in dynamic trampolines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit a17dc12bfed8868e6a86f3b45c16065a70641acb upstream. With CONFIG_CALL_DEPTH_TRACKING enabled on an x86 retbleed-affected platform (eg: Skylake), with retbleed=stuff, registering a dynamic ftrace trampoline crashes on the first call into the traced function: BUG: unable to handle page fault for address: ffff88817ae18880 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 4b53067 P4D 4b53067 PUD 0 Oops: Oops: 0002 [#1] SMP PTI CPU: 3 UID: 0 PID: 187 Comm: usleep Not tainted 7.0.10 #243 PREEMPT(full) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.17.0-2-2 04/01/2014 Code: 24 78 00 00 00 00 48 89 ea 48 89 54 24 20 48 8b b4 24 b8 00 00 00 48 8b bc 24 b0 00 00 00 48 89 bc 24 80 00 00 00 48 83 ef 05 <65> 48 c1 3d 1f a8 b6 02 05 48 8b 15 f6 00 00 00 4c 89 3c 24 4c 89 Call Trace: ? find_held_lock ? exc_page_fault ? lock_release ? __x64_sys_clock_nanosleep ? lockdep_hardirqs_on_prepare ? trace_hardirqs_on __x64_sys_clock_nanosleep do_syscall_64 ? exc_page_fault ? call_depth_return_thunk entry_SYSCALL_64_after_hwframe ... Kernel panic - not syncing: Fatal exception This small reproducer allows to easily trigger the crash: # echo 'p __x64_sys_clock_nanosleep' > /sys/kernel/tracing/kprobe_events # echo 1 > /sys/kernel/tracing/events/kprobes/p___x64_sys_clock_nanosleep_0/enable # usleep 1 Monitoring the crash under GDB points to the exact instruction in charge of incrementing the call depth: sarq $5, %gs:__x86_call_depth(%rip) This instruction matches the one inserted by the ftrace_regs_caller from ftrace_64.S. This emitted code was likely working fine until the introduction of 59bec00ace28 ("x86/percpu: Introduce %rip-relative addressing to PER_CPU_VAR()"): it has made the call depth accounting addressing relative to $rip, instead of being based on an absolute address. As this code exact location depends on where the trampoline lives in memory, the corresponding displacement needs to be adjusted at runtime to actually correctly find the per-cpu __x86_call_depth value, otherwise the targeted address is wrong, leading to the page fault seen above. Fix the %rip-relative displacement of the copied CALL_DEPTH_ACCOUNT instruction (from ftrace_regs_caller) by calling text_poke_apply_relocation(), as it is done for example by the x86 BPF JIT compiler through x86_call_depth_emit_accounting(). This corrects both CALL_DEPTH_ACCOUNT slots, in ftrace_caller and ftrace_regs_caller. [ bp: Massage. ] Fixes: 59bec00ace28 ("x86/percpu: Introduce %rip-relative addressing to PER_CPU_VAR()") Signed-off-by: Alexis Lothoré (eBPF Foundation) Signed-off-by: Borislav Petkov (AMD) Acked-by: Peter Zijlstra (Intel) Acked-by: Steven Rostedt Cc: Link: https://patch.msgid.link/20260527-fix_call_depth_in_trampoline-v1-1-1c1abc8ae310@bootlin.com Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/ftrace.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 4450acec9390..41d6807cec41 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -370,6 +370,13 @@ create_trampoline(struct ftrace_ops *ops, unsigned int *tramp_size) goto fail; } + /* + * Generated trampoline may contain rIP-relative addressing which + * displacement needs to be fixed. + */ + text_poke_apply_relocation(trampoline, trampoline, size, + (void *)start_offset, size); + /* * The address of the ftrace_ops that is used for this trampoline * is stored at the end of the trampoline. This will be used to From e4892b1ecd73d6690424b075feea48e05aaa0ad0 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Fri, 29 May 2026 19:31:34 +0200 Subject: [PATCH 561/697] wireguard: send: append trailer after expanding head commit f75e3eb08fe31d30a9af6ed80cdd22e6772837e2 upstream. With how this is currently written, we add the trailer, zero it out, and then add the header space on. If that header space requires a reallocation + copy, the zeros in the trailer aren't copied, because the skb len hasn't actually been yet expanded to cover that. Instead add the padding at the end of the process rather than at the beginning. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Cc: stable@vger.kernel.org Signed-off-by: Jason A. Donenfeld Link: https://patch.msgid.link/20260529173134.3080773-2-Jason@zx2c4.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireguard/send.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireguard/send.c b/drivers/net/wireguard/send.c index 26e09c30d596..67d01478eb76 100644 --- a/drivers/net/wireguard/send.c +++ b/drivers/net/wireguard/send.c @@ -177,16 +177,6 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair) trailer_len = padding_len + noise_encrypted_len(0); plaintext_len = skb->len + padding_len; - /* Expand data section to have room for padding and auth tag. */ - num_frags = skb_cow_data(skb, trailer_len, &trailer); - if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg))) - return false; - - /* Set the padding to zeros, and make sure it and the auth tag are part - * of the skb. - */ - memset(skb_tail_pointer(trailer), 0, padding_len); - /* Expand head section to have room for our header and the network * stack's headers. */ @@ -198,6 +188,16 @@ static bool encrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair) skb_checksum_help(skb))) return false; + /* Expand data section to have room for padding and auth tag. */ + num_frags = skb_cow_data(skb, trailer_len, &trailer); + if (unlikely(num_frags < 0 || num_frags > ARRAY_SIZE(sg))) + return false; + + /* Set the padding to zeros, and make sure it and the auth tag are part + * of the skb. + */ + memset(skb_tail_pointer(trailer), 0, padding_len); + /* Only after checksumming can we safely add on the padding at the end * and the header. */ From 5e19028667963fb371ebb00cecc2a473ef92056b Mon Sep 17 00:00:00 2001 From: Yuqi Xu Date: Wed, 27 May 2026 11:48:15 +0800 Subject: [PATCH 562/697] bpf: sockmap: fix tail fragment offset in bpf_msg_push_data commit f72eed9b84fb771019a955908132410a9ba9ea3f upstream. When bpf_msg_push_data() inserts data in the middle of a scatterlist entry, it splits the original entry into a left fragment and a right fragment. The right fragment offset is page-local, but the code advances it with `start`, which is the message-global insertion point. For inserts into a non-first SG entry, this over-advances the offset and leaves the split layout inconsistent. Advance the right fragment offset by the fragment-local delta, `start - offset`, which matches the length removed from the front of the original entry. Fixes: 6fff607e2f14 ("bpf: sk_msg program helper bpf_msg_push_data") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Yuqi Xu Signed-off-by: Ren Wei Link: https://patch.msgid.link/8b129d10566aa3eb43f61a8f9757bcf51707d324.1779636774.git.xuyq21@lenovo.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/core/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/filter.c b/net/core/filter.c index bad3fb631846..e6dd40e0276e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2859,7 +2859,7 @@ BPF_CALL_4(bpf_msg_push_data, struct sk_msg *, msg, u32, start, psge->length = start - offset; rsge.length -= psge->length; - rsge.offset += start; + rsge.offset += start - offset; sk_msg_iter_var_next(i); sg_unmark_end(psge); From 6d00f5c7e5ff7ec4795b7f5f8ed88bd346641652 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Wed, 20 May 2026 11:47:55 +0800 Subject: [PATCH 563/697] macsec: fix replay protection at XPN lower-PN wrap commit e68842b3356471ba56c882209f324613dac47f64 upstream. In macsec_post_decrypt(), when pn is U32_MAX, pn + 1 overflows u32 to 0 and the first branch never fires. If next_pn_halves.lower is also in the upper half, pn_same_half(pn, lower) is true and the XPN else-if does not fire either, leaving next_pn_halves unchanged. An attacker that captures the legitimate frame carrying pn == 0xFFFFFFFF on an XPN association can then replay it indefinitely, since lowest_pn never rises above the captured pn and macsec_decrypt() reconstructs the same IV. Extend the XPN else-if to also fire when pn + 1 wraps to 0, so receipt of pn == U32_MAX advances next_pn_halves to (upper + 1, 0). Fixes: a21ecf0e0338 ("macsec: Support XPN frame handling - IEEE 802.1AEbw") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Link: https://patch.msgid.link/SYBPR01MB78813FD49E58F253B989F197AF012@SYBPR01MB7881.ausprd01.prod.outlook.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/net/macsec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 5d820ef61946..ee1b565efca4 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -804,7 +804,8 @@ static bool macsec_post_decrypt(struct sk_buff *skb, struct macsec_secy *secy, u if (pn + 1 > rx_sa->next_pn_halves.lower) { rx_sa->next_pn_halves.lower = pn + 1; } else if (secy->xpn && - !pn_same_half(pn, rx_sa->next_pn_halves.lower)) { + (pn + 1 == 0 || + !pn_same_half(pn, rx_sa->next_pn_halves.lower))) { rx_sa->next_pn_halves.upper++; rx_sa->next_pn_halves.lower = pn + 1; } From c512e1c819dfbf6ae95ee7a44b65b9ad98979157 Mon Sep 17 00:00:00 2001 From: Justin Iurman Date: Fri, 22 May 2026 13:20:13 +0200 Subject: [PATCH 564/697] ipv6: exthdrs: refresh nh pointer after ipv6_hop_jumbo() commit d47548a36639095939f4747d4c43f2271366f565 upstream. ipv6_hop_jumbo() calls pskb_trim_rcsum(), which can change skb pointers. Let's recompute nh pointer to make sure any change won't mess things up. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Justin Iurman Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260522112013.12342-1-justin.iurman@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/exthdrs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 830131b427f0..4c9d6e755dfc 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -184,6 +184,8 @@ static bool ip6_parse_tlv(bool hopbyhop, case IPV6_TLV_JUMBO: if (!ipv6_hop_jumbo(skb, off)) return false; + + nh = skb_network_header(skb); break; case IPV6_TLV_CALIPSO: if (!ipv6_hop_calipso(skb, off)) From 90983f841dfa9fec19b4e0d560a1d3bcf98da633 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 18 May 2026 09:23:45 +0000 Subject: [PATCH 565/697] ASoC: qcom: q6asm-dai: fix error handling in prepare and set_params commit 4b4db09f283df65d780bc7cee66cb4a7e9bf4770 upstream. Fix error handling in q6asm_dai_compr_set_params() and q6asm_dai_prepare() for both CMD_CLOSE and q6asm_unmap_memory_regions(). In both the functions, we are doing q6asm_audio_client_free in failure cases, which means if prepare or set_params fail, we can never recover. Now open and close are done in respective dai_open/close functions. Fixes: 2a9e92d371db ("ASoC: qdsp6: q6asm: Add q6asm dai driver") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260518092347.3446946-4-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/qcom/qdsp6/q6asm-dai.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 0eae8c6e42b8..b1e26cc927f1 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -235,9 +235,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, /* rate and channels are sent to audio driver */ if (prtd->state == Q6ASM_STREAM_RUNNING) { /* clear the previous setup if any */ - q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); - q6asm_unmap_memory_regions(substream->stream, - prtd->audio_client); + ret = q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); + if (ret < 0) { + dev_err(dev, "Failed to close q6asm stream %d\n", prtd->stream_id); + return ret; + } + + ret = q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); + if (ret < 0) { + dev_err(dev, "Failed to unmap memory regions for q6asm stream %d\n", + prtd->stream_id); + return ret; + } + q6routing_stream_close(soc_prtd->dai_link->id, substream->stream); prtd->state = Q6ASM_STREAM_STOPPED; @@ -305,8 +315,6 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); open_err: q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; return ret; } @@ -903,7 +911,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, prtd->session_id, dir); if (ret) { dev_err(dev, "Stream reg failed ret:%d\n", ret); - goto q6_err; + goto routing_err; } ret = __q6asm_dai_compr_set_codec_params(component, stream, @@ -929,11 +937,11 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return 0; q6_err: + q6routing_stream_close(rtd->dai_link->id, dir); +routing_err: q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); open_err: - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; return ret; } From 751db1b802a067b7fff25880f4e9f9152a171538 Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Fri, 22 May 2026 17:42:26 +0800 Subject: [PATCH 566/697] ipv6: exthdrs: refresh nh after handling HAO option commit f7b52afe3592eae66e160586b45a3f2242972c63 upstream. ip6_parse_tlv() caches skb_network_header(skb) in nh while walking IPv6 TLVs. ipv6_dest_hao() may call pskb_expand_head() for a cloned skb, which can move the skb head and invalidate the cached network header pointer. Refresh nh after ipv6_dest_hao() returns so any trailing padding or TLVs are parsed from the current skb head. This matches the existing pattern used in ip6_parse_tlv() after helpers that can modify skb header storage. Fixes: a831f5bbc89a ("[IPV6] MIP6: Add inbound interface of home address option.") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Reviewed-by: Justin Iurman Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/7aba1debc2196189172499e5769802b026f8caf8.1779247873.git.zcliangcn@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/exthdrs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 4c9d6e755dfc..fbc0df2daed3 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -203,6 +203,8 @@ static bool ip6_parse_tlv(bool hopbyhop, case IPV6_TLV_HAO: if (!ipv6_dest_hao(skb, off)) return false; + + nh = skb_network_header(skb); break; #endif default: From 853f6ea482dfcd3404bbef458ab4d68364eed838 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Thu, 21 May 2026 21:05:55 +0800 Subject: [PATCH 567/697] ip6: vti: Use ip6_tnl.net in vti6_siocdevprivate(). commit 8b484efd5cb4eeef9021a661e198edc5349dacf6 upstream. After patch 1/2 in this series, vti6_update() unlinks and relinks the tunnel through t->net. vti6_siocdevprivate() still uses dev_net(dev) for the collision lookup. For a tunnel moved through IFLA_NET_NS_FD, dev_net(dev) is the new netns, not t->net. SIOCCHGTUNNEL on a migrated tunnel then runs: net = dev_net(dev) /* migrated netns */ t = vti6_locate(net, &p1, false) /* misses target in t->net */ ... t = netdev_priv(dev) vti6_update(t, &p1, false) /* mutates t->net's hash */ A caller in the migrated netns picks params that match a tunnel in the creation netns. The lookup in dev_net(dev) finds nothing. vti6_update() prepends the migrated tunnel at the head of the creation netns hash bucket for those params. Later lookups in the creation netns resolve to the migrated device. xfrm receive delivers the matched packets through a device the caller controls. Reachable from an unprivileged user namespace (unshare --user --map-root-user --net). Cross tenant scope on container hosts. Switch the SIOCCHGTUNNEL path on a non fallback device to use t->net for the lookup. The lookup now matches the netns vti6_update() operates on. Also add ns_capable(self->net->user_ns, CAP_NET_ADMIN) before the lookup. The check at the top of the case is against dev_net(dev)->user_ns, which after migration is the attacker's netns. A caller there can pick params absent from self->net, the lookup returns NULL, t becomes self, and vti6_update() inserts the device into the creation netns hash. The new check requires CAP_NET_ADMIN in the creation netns user_ns too. SIOCADDTUNNEL and SIOCCHGTUNNEL on the fallback device keep dev_net(dev), which equals init_net there. Fixes: 61220ab34948 ("vti6: Enable namespace changing") Suggested-by: Jakub Kicinski Suggested-by: Xiao Liang Cc: stable@vger.kernel.org # v5.15+ Signed-off-by: Maoyi Xie Link: https://patch.msgid.link/20260521130555.3421684-3-maoyixie.tju@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_vti.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index ad5290be4dd6..cd9a5ba10189 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -834,17 +834,24 @@ vti6_siocdevprivate(struct net_device *dev, struct ifreq *ifr, void __user *data if (p.proto != IPPROTO_IPV6 && p.proto != 0) break; vti6_parm_from_user(&p1, &p); - t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL); if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { + struct ip6_tnl *self = netdev_priv(dev); + + err = -EPERM; + if (!ns_capable(self->net->user_ns, CAP_NET_ADMIN)) + break; + t = vti6_locate(self->net, &p1, false); if (t) { if (t->dev != dev) { err = -EEXIST; break; } } else - t = netdev_priv(dev); + t = self; err = vti6_update(t, &p1, false); + } else { + t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL); } if (t) { err = 0; From a35daeabb433686234b010ebf7b53778dbd6c9b8 Mon Sep 17 00:00:00 2001 From: Qi Tang Date: Sat, 23 May 2026 22:32:45 +0800 Subject: [PATCH 568/697] ipv6: validate extension header length before copying to cmsg commit dd433671fef381fdaf7b530c631e6b782d66e224 upstream. ip6_datagram_recv_specific_ctl() builds IPV6_{HOPOPTS,DSTOPTS,RTHDR} cmsgs (and their IPV6_2292* legacy counterparts) by trusting the on-wire hdrlen byte (ptr[1]) when computing the put_cmsg() length. The length was validated only at parse time (ipv6_parse_hopopts(), etc.). An nftables payload-write expression can rewrite hdrlen after parsing and before the skb reaches recvmsg; the write itself is in-bounds but put_cmsg() then reads up to ((hdrlen+1) << 3) = 2040 bytes from an 8-byte header. nftables is reachable from an unprivileged user namespace, so this is an unprivileged slab-out-of-bounds read: BUG: KASAN: slab-out-of-bounds in put_cmsg+0x3ac/0x540 put_cmsg+0x3ac/0x540 udpv6_recvmsg+0xca0/0x1250 sock_recvmsg+0xdf/0x190 ____sys_recvmsg+0x1b1/0x620 Add ipv6_get_exthdr_len() which validates that at least two bytes are accessible before reading the hdrlen field, then checks the computed length against skb_tail_pointer(skb), returning 0 on failure. Extension headers are kept in the linear skb area by pskb_may_pull() during input, so skb_tail_pointer() is the correct bound. Use ipv6_get_exthdr_len() at all non-AH call sites: the five standalone cmsg blocks (HbH, 2292HbH, 2292DSTOPTS x2, 2292RTHDR) and the three standard cases in the extension-header walk loop (DSTOPTS, ROUTING, default). AH retains an inline bounds check because its length formula differs ((ptr[1]+2)<<2). The walk loop also gets a pre-read bounds check at the top to validate ptr before any case accesses ptr[0] or ptr[1]. When the walk loop detects a corrupted header, return from the function instead of continuing to process later socket options. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Qi Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260523143245.2281415-1-tpluszz77@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/ipv6/datagram.c | 54 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 8933fa4c3dd5..a62c20e8329f 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -617,6 +617,18 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg, } } +static u16 ipv6_get_exthdr_len(const struct sk_buff *skb, const u8 *ptr) +{ + u16 len; + + if (ptr + 2 > skb_tail_pointer(skb)) + return 0; + + len = (ptr[1] + 1) << 3; + + return (len <= skb_tail_pointer(skb) - ptr) ? len : 0; +} + void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { @@ -643,7 +655,10 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, /* HbH is allowed only once */ if (np->rxopt.bits.hopopts && (opt->flags & IP6SKB_HOPBYHOP)) { u8 *ptr = nh + sizeof(struct ipv6hdr); - put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); + u16 len = ipv6_get_exthdr_len(skb, ptr); + + if (len) + put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, len, ptr); } if (opt->lastopt && @@ -664,26 +679,37 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, unsigned int len; u8 *ptr = nh + off; + if (ptr + 2 > skb_tail_pointer(skb)) + return; + switch (nexthdr) { case IPPROTO_DSTOPTS: nexthdr = ptr[0]; - len = (ptr[1] + 1) << 3; + len = ipv6_get_exthdr_len(skb, ptr); + if (!len) + return; if (np->rxopt.bits.dstopts) put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, len, ptr); break; case IPPROTO_ROUTING: nexthdr = ptr[0]; - len = (ptr[1] + 1) << 3; + len = ipv6_get_exthdr_len(skb, ptr); + if (!len) + return; if (np->rxopt.bits.srcrt) put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, len, ptr); break; case IPPROTO_AH: nexthdr = ptr[0]; len = (ptr[1] + 2) << 2; + if (ptr + len > skb_tail_pointer(skb)) + return; break; default: nexthdr = ptr[0]; - len = (ptr[1] + 1) << 3; + len = ipv6_get_exthdr_len(skb, ptr); + if (!len) + return; break; } @@ -705,19 +731,31 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, } if (np->rxopt.bits.ohopopts && (opt->flags & IP6SKB_HOPBYHOP)) { u8 *ptr = nh + sizeof(struct ipv6hdr); - put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, (ptr[1]+1)<<3, ptr); + u16 len = ipv6_get_exthdr_len(skb, ptr); + + if (len) + put_cmsg(msg, SOL_IPV6, IPV6_2292HOPOPTS, len, ptr); } if (np->rxopt.bits.odstopts && opt->dst0) { u8 *ptr = nh + opt->dst0; - put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); + u16 len = ipv6_get_exthdr_len(skb, ptr); + + if (len) + put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr); } if (np->rxopt.bits.osrcrt && opt->srcrt) { struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(nh + opt->srcrt); - put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, (rthdr->hdrlen+1) << 3, rthdr); + u16 len = ipv6_get_exthdr_len(skb, (u8 *)rthdr); + + if (len) + put_cmsg(msg, SOL_IPV6, IPV6_2292RTHDR, len, rthdr); } if (np->rxopt.bits.odstopts && opt->dst1) { u8 *ptr = nh + opt->dst1; - put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, (ptr[1]+1)<<3, ptr); + u16 len = ipv6_get_exthdr_len(skb, ptr); + + if (len) + put_cmsg(msg, SOL_IPV6, IPV6_2292DSTOPTS, len, ptr); } if (np->rxopt.bits.rxorigdstaddr) { struct sockaddr_in6 sin6; From 9f7ebb45a83afc3216e855e57d51bb4bc9b5232e Mon Sep 17 00:00:00 2001 From: Zhengchuan Liang Date: Fri, 22 May 2026 17:31:55 +0800 Subject: [PATCH 569/697] xfrm: input: hold netns during deferred transport reinjection commit c16f74dc1d75d0e2e7670076d5375deda110ebeb upstream. Transport-mode reinjection stores a struct net pointer in skb->cb and uses it later from xfrm_trans_reinject(). That pointer must stay valid until the deferred callback runs. Take a netns reference when queueing deferred reinjection work and drop it after the callback completes. Use maybe_get_net() so the queueing path does not revive a namespace that is already being torn down. This keeps the existing workqueue design and fixes the netns lifetime handling in one place for all users of xfrm_trans_queue_net(). Fixes: 7b3801927e52 ("xfrm: introduce xfrm_trans_queue_net") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Xin Liu Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Zhengchuan Liang Signed-off-by: Ren Wei Assisted-by: Codex:gpt-5.4 Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_input.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 3d80c5210e04..cfee63a6c87d 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -794,9 +794,12 @@ static void xfrm_trans_reinject(struct work_struct *work) spin_unlock_bh(&trans->queue_lock); local_bh_disable(); - while ((skb = __skb_dequeue(&queue))) - XFRM_TRANS_SKB_CB(skb)->finish(XFRM_TRANS_SKB_CB(skb)->net, - NULL, skb); + while ((skb = __skb_dequeue(&queue))) { + struct net *net = XFRM_TRANS_SKB_CB(skb)->net; + + XFRM_TRANS_SKB_CB(skb)->finish(net, NULL, skb); + put_net(net); + } local_bh_enable(); } @@ -805,6 +808,7 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb, struct sk_buff *)) { struct xfrm_trans_tasklet *trans; + struct net *hold_net; trans = this_cpu_ptr(&xfrm_trans_tasklet); @@ -813,8 +817,12 @@ int xfrm_trans_queue_net(struct net *net, struct sk_buff *skb, BUILD_BUG_ON(sizeof(struct xfrm_trans_cb) > sizeof(skb->cb)); + hold_net = maybe_get_net(net); + if (!hold_net) + return -ENODEV; + XFRM_TRANS_SKB_CB(skb)->finish = finish; - XFRM_TRANS_SKB_CB(skb)->net = net; + XFRM_TRANS_SKB_CB(skb)->net = hold_net; spin_lock_bh(&trans->queue_lock); __skb_queue_tail(&trans->queue, skb); spin_unlock_bh(&trans->queue_lock); From 947013fd7c8c35dd5856557b215840098a3f67f8 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 22 May 2026 22:34:23 -0400 Subject: [PATCH 570/697] l2tp: use refcount_inc_not_zero in l2tp_session_get_by_ifname commit 05f95729ca844704d15e49ce14868af4b403b32b upstream. A reader in l2tp_session_get_by_ifname() can return a pointer to a session whose refcount has reached zero. The getter takes its reference with plain refcount_inc(), but every other session getter in the same file (l2tp_v2_session_get, l2tp_v3_session_get, and the corresponding _get_next variants) uses refcount_inc_not_zero() because the IDR/RCU lookup can race with refcount_dec_and_test() -> l2tp_session_free() -> kfree_rcu(). The ifname getter is the only outlier; the inconsistency was raised on-list after 979c017803c4 ("l2tp: use list_del_rcu in l2tp_session_unhash"). A reader inside rcu_read_lock_bh() that matches session->ifname can be preempted between the strcmp() and the refcount_inc(). If the last reference drops on another CPU in that window, the reader's refcount_inc() runs on a counter that has reached zero. refcount_t catches the addition-on-zero, prints "refcount_t: addition on 0; use-after-free", saturates the counter, and returns the saturated pointer to the caller. Session memory is held live by the in-flight RCU read section, but the kfree_rcu() callback queued from l2tp_session_free() will free it once the grace period closes; a caller that dereferences the returned session past that point hits a slab-use-after-free. On PREEMPT_RT local_bh_disable() is a per-CPU sleeping lock and the preemption window is real; on stock PREEMPT kernels local_bh_disable() is a preempt_count increment that closes the cross-CPU race in practice (see below). Use refcount_inc_not_zero() and continue the list walk on failure, matching the other session getters in the file. The ifname getter is the only session getter in net/l2tp/ that still uses the bare refcount_inc() pattern; this change restores file-internal consistency. The success path is unchanged. Fixes: abe7a1a7d0b6 ("l2tp: improve tunnel/session refcount helpers") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito Reviewed-by: James Chapman Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260523023423.2568972-1-michael.bommarito@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- net/l2tp/l2tp_core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index be32bf45d583..fd78928e9be2 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -441,12 +441,13 @@ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net, idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) { if (tunnel) { list_for_each_entry_rcu(session, &tunnel->session_list, list) { - if (!strcmp(session->ifname, ifname)) { - refcount_inc(&session->ref_count); - rcu_read_unlock_bh(); + if (strcmp(session->ifname, ifname)) + continue; + if (!refcount_inc_not_zero(&session->ref_count)) + continue; + rcu_read_unlock_bh(); - return session; - } + return session; } } } From fc32be9ac2788524c6b24efd681cce7a6e731a92 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Thu, 21 May 2026 21:05:54 +0800 Subject: [PATCH 571/697] ip6: vti: Use ip6_tnl.net in vti6_changelink(). commit 11b326fb0a374f4654f9be22d0f0f7abd9f7d3fe upstream. ip netns add ns1 ip netns add ns2 ip -n ns1 link add vti6_test type vti6 remote ::1 local ::2 key 7 ip -n ns1 link set vti6_test netns ns2 ip -n ns2 link set vti6_test type vti6 remote ::3 local ::4 key 9 ip netns del ns2 ip netns del ns1 [ 132.495484] ------------[ cut here ]------------ [ 132.497609] kernel BUG at net/core/dev.c:12376! Commit 61220ab34948 ("vti6: Enable namespace changing") dropped NETIF_F_NETNS_LOCAL from vti6 devices. A vti6 tunnel can then move through IFLA_NET_NS_FD. After the move dev_net(dev) points at the new netns while t->net stays at the creation netns. vti6_changelink() and vti6_update() still use dev_net(dev) and dev_net(t->dev). They unlink from one per netns hash and relink into another. The creation netns is left with a stale entry. cleanup_net() of that netns later walks freed memory. Reachable from an unprivileged user namespace (unshare --user --map-root-user --net). Cross tenant scope on container hosts. Fixes: 61220ab34948 ("vti6: Enable namespace changing") Reported-by: Maoyi Xie Reviewed-by: Eric Dumazet Cc: stable@vger.kernel.org # v5.15+ Signed-off-by: Kuniyuki Iwashima Link: https://patch.msgid.link/20260521130555.3421684-2-maoyixie.tju@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/ipv6/ip6_vti.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index cd9a5ba10189..df793c8bfffb 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -722,10 +722,11 @@ vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p, static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p, bool keep_mtu) { - struct net *net = dev_net(t->dev); - struct vti6_net *ip6n = net_generic(net, vti6_net_id); + struct net *net = t->net; + struct vti6_net *ip6n; int err; + ip6n = net_generic(net, vti6_net_id); vti6_tnl_unlink(ip6n, t); synchronize_net(); err = vti6_tnl_change(t, p, keep_mtu); @@ -1038,11 +1039,12 @@ static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - struct ip6_tnl *t; + struct ip6_tnl *t = netdev_priv(dev); + struct net *net = t->net; struct __ip6_tnl_parm p; - struct net *net = dev_net(dev); - struct vti6_net *ip6n = net_generic(net, vti6_net_id); + struct vti6_net *ip6n; + ip6n = net_generic(net, vti6_net_id); if (dev == ip6n->fb_tnl_dev) return -EINVAL; From 96a4713ae041cc85e712bac682cd2e644004d6c6 Mon Sep 17 00:00:00 2001 From: Minh Nguyen Date: Tue, 26 May 2026 11:12:39 +0700 Subject: [PATCH 572/697] net: skbuff: fix missing zerocopy reference in pskb_carve helpers commit 98d0912e9f841e5529a5b89a972805f34cb1c69d upstream. pskb_carve_inside_header() and pskb_carve_inside_nonlinear() both copy the old skb_shared_info header into a new buffer via memcpy(), which includes the destructor_arg pointer (uarg) for MSG_ZEROCOPY skbs. Neither function calls net_zcopy_get() for the new shinfo, creating an unaccounted holder: every skb_shared_info with destructor_arg set will call skb_zcopy_clear() once when freed, but the corresponding net_zcopy_get() was never called for the new copy. Repeated calls drive uarg->refcnt to zero prematurely, freeing ubuf_info_msgzc while TX skbs still hold live destructor_arg pointers. KASAN reports use-after-free on a freed ubuf_info_msgzc: BUG: KASAN: slab-use-after-free in skb_release_data+0x77b/0x810 Read of size 8 at addr ffff88801574d3e8 by task poc/220 Call Trace: skb_release_data+0x77b/0x810 kfree_skb_list_reason+0x13e/0x610 skb_release_data+0x4cd/0x810 sk_skb_reason_drop+0xf3/0x340 skb_queue_purge_reason+0x282/0x440 rds_tcp_inc_free+0x1e/0x30 rds_recvmsg+0x354/0x1780 __sys_recvmsg+0xdf/0x180 Allocated by task 219: msg_zerocopy_realloc+0x157/0x7b0 tcp_sendmsg_locked+0x2892/0x3ba0 Freed by task 219: ip_recv_error+0x74a/0xb10 tcp_recvmsg+0x475/0x530 The skb consuming the late access still referenced the same uarg via shinfo->destructor_arg copied by pskb_carve_inside_nonlinear() without a refcount bump. This has been verified to be reliably exploitable: a working proof-of-concept achieves full root privilege escalation from an unprivileged local user on a default kernel configuration. The fix follows the pattern of pskb_expand_head() which has the same memcpy/cloned structure. For pskb_carve_inside_header(), net_zcopy_get() is placed after skb_orphan_frags() succeeds, so the orphan error path needs no cleanup. For pskb_carve_inside_nonlinear(), net_zcopy_get() is placed after all failure points and just before skb_release_data(), so no error path needs cleanup at all -- matching pskb_expand_head() more closely and avoiding the need for a balancing net_zcopy_put(). Fixes: 6fa01ccd8830 ("skbuff: Add pskb_extract() helper function") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Minh Nguyen Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260526041240.329462-1-minhnguyen.080505@gmail.com Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- net/core/skbuff.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a52aa79b0fdb..745bb0a67c6a 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -6787,6 +6787,8 @@ static int pskb_carve_inside_header(struct sk_buff *skb, const u32 off, skb_kfree_head(data, size); return -ENOMEM; } + if (skb_zcopy(skb)) + net_zcopy_get(skb_zcopy(skb)); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); if (skb_has_frag_list(skb)) @@ -6935,6 +6937,8 @@ static int pskb_carve_inside_nonlinear(struct sk_buff *skb, const u32 off, skb_kfree_head(data, size); return -ENOMEM; } + if (skb_zcopy(skb)) + net_zcopy_get(skb_zcopy(skb)); skb_release_data(skb, SKB_CONSUMED); skb->head = data; From 59139473a7a73647830a09edddf847888496511b Mon Sep 17 00:00:00 2001 From: Santhosh Kumar K Date: Wed, 27 May 2026 23:07:36 +0530 Subject: [PATCH 573/697] spi: spi-mem: avoid mutating op template in spi_mem_supports_op() commit 79378db6a86c7014cce40b65252e6c18f5b8bcc2 upstream. spi_mem_supports_op() accepts a const struct spi_mem_op pointer but casts away const internally to call spi_mem_adjust_op_freq(). This mutates the caller's op template, which causes stale max_freq values when callers reuse persistent templates - subsequent calls won't re-apply the device frequency cap since spi_mem_adjust_op_freq() skips non-zero values. Fix by operating on a stack-local copy instead. Fixes: a4f8e70d75dd ("spi: spi-mem: add spi_mem_adjust_op_freq() in spi_mem_supports_op()") Cc: Tianyu Xu Cc: stable@vger.kernel.org Signed-off-by: Santhosh Kumar K Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/20260527173736.2243004-1-s-k6@ti.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- drivers/spi/spi-mem.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index db9467535416..65afb0a6b0a8 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -276,13 +276,20 @@ static bool spi_mem_internal_supports_op(struct spi_mem *mem, */ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - /* Make sure the operation frequency is correct before going futher */ - spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op); + struct spi_mem_op eval_op = *op; + + /* + * Work on a local copy; this is a pure capability check and must + * not modify the caller's op. Stored templates with max_freq == 0 + * must remain unset so their frequency is always re-capped to the + * current device maximum at execution time. + */ + spi_mem_adjust_op_freq(mem, &eval_op); - if (spi_mem_check_op(op)) + if (spi_mem_check_op(&eval_op)) return false; - return spi_mem_internal_supports_op(mem, op); + return spi_mem_internal_supports_op(mem, &eval_op); } EXPORT_SYMBOL_GPL(spi_mem_supports_op); From b8338111e14183972359009c12d0dbd81d2e1e16 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 27 May 2026 17:05:26 +0100 Subject: [PATCH 574/697] HID: wacom: Fix OOB write in wacom_hid_set_device_mode() commit c0a8899e02ddebd51e2589835182c239c2e224ae upstream. wacom_hid_set_device_mode() currently assumes that the HID_DG_INPUTMODE usage is always located in the first field (field[0]) of the feature report. However, a device can specify HID_DG_INPUTMODE in a different field. If HID_DG_INPUTMODE is in a field other than the first one and the first field has a report_count smaller than the usage_index of HID_DG_INPUTMODE, this leads to an out-of-bounds write to r->field[0]->value. Fix this by storing the field index of HID_DG_INPUTMODE in 'struct hid_data' during feature mapping. In wacom_hid_set_device_mode(), use this stored field index to access the correct field and add bounds checks to ensure both the field index and the value index are within valid ranges before writing. Cc: stable@vger.kernel.org Fixes: 5ae6e89f7409 ("HID: wacom: implement the finger part of the HID generic handling") Tested-by: Ping Cheng Reviewed-by: Ping Cheng Signed-off-by: Lee Jones Signed-off-by: Benjamin Tissoires Signed-off-by: Greg Kroah-Hartman --- drivers/hid/wacom_sys.c | 13 ++++++++++--- drivers/hid/wacom_wac.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index a448cfcca4c4..63b5fed9415d 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -356,6 +356,7 @@ static void wacom_feature_mapping(struct hid_device *hdev, hid_data->inputmode = field->report->id; hid_data->inputmode_index = usage->usage_index; + hid_data->inputmode_field_index = field->index; break; case HID_UP_DIGITIZER: @@ -571,9 +572,14 @@ static int wacom_hid_set_device_mode(struct hid_device *hdev) re = &(hdev->report_enum[HID_FEATURE_REPORT]); r = re->report_id_hash[hid_data->inputmode]; - if (r) { - r->field[0]->value[hid_data->inputmode_index] = 2; - hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + if (r && hid_data->inputmode_field_index >= 0 && + hid_data->inputmode_field_index < r->maxfield) { + struct hid_field *field = r->field[hid_data->inputmode_field_index]; + + if (field && hid_data->inputmode_index < field->report_count) { + field->value[hid_data->inputmode_index] = 2; + hid_hw_request(hdev, r, HID_REQ_SET_REPORT); + } } return 0; } @@ -2846,6 +2852,7 @@ static int wacom_probe(struct hid_device *hdev, return -ENODEV; wacom_wac->hid_data.inputmode = -1; + wacom_wac->hid_data.inputmode_field_index = -1; wacom_wac->mode_report = -1; if (hid_is_usb(hdev)) { diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index d4f7d8ca1e7e..126bec6e5c0c 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -295,6 +295,7 @@ struct wacom_shared { struct hid_data { __s16 inputmode; /* InputMode HID feature, -1 if non-existent */ __s16 inputmode_index; /* InputMode HID feature index in the report */ + __s16 inputmode_field_index; /* InputMode HID feature field index in the report */ bool sense_state; bool inrange_state; bool eraser; From 8b1f4f618fd8531744e87abc1eb5bab2193e6356 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 May 2026 16:53:54 +0200 Subject: [PATCH 575/697] iommu, debugobjects: avoid gcc-16.1 section mismatch warnings commit 4c9ad387aa2d6785299722e54224d34764edaeb3 upstream. gcc-16 has gained some more advanced inter-procedual optimization techniques that enable it to inline the dummy_tlb_add_page() and dummy_tlb_flush() function pointers into a specialized version of __arm_v7s_unmap: WARNING: modpost: vmlinux: section mismatch in reference: __arm_v7s_unmap+0x2cc (section: .text) -> dummy_tlb_add_page (section: .init.text) ERROR: modpost: Section mismatches detected. >From what I can tell, the transformation is correct, as this is only called when __arm_v7s_unmap() is called from arm_v7s_do_selftests(), which is also __init. Since __arm_v7s_unmap() however is not __init, gcc cannot inline the inner function calls directly. In debug_objects_selftest(), the same thing happens. Both the caller and the leaf function are __init, but the IPA pulls it into a non-init one: WARNING: modpost: vmlinux: section mismatch in reference: lookup_object_or_alloc+0x7c (section: .text.lookup_object_or_alloc) -> is_static_object (section: .init.text) Marking the affected functions as not "__init" would reliably avoid this issue but is not a good solution because it removes an otherwise correct annotation. I tried marking the functions as 'noinline', but that ended up not covering all the affected configurations. With some more experimenting, I found that marking these functions as __attribute__((noipa)) is both logical and reliable. In order to keep the syntax readable, add a custom macro for this in include/linux/compiler_attributes.h next to other related macros and use it to annotate both files. Link: https://lore.kernel.org/all/abRB6g-48ZX6Yl2r@willie-the-truck/ Cc: Will Deacon Cc: Thomas Gleixner Cc: Andrew Morton Cc: Miguel Ojeda Cc: linux-kbuild@vger.kernel.org Cc: stable@vger.kernel.org Signed-off-by: Arnd Bergmann Acked-by: Will Deacon Acked-by: Thomas Gleixner Acked-by: Miguel Ojeda Signed-off-by: Joerg Roedel Signed-off-by: Greg Kroah-Hartman --- drivers/iommu/io-pgtable-arm-v7s.c | 18 ++++++++++++------ include/linux/compiler_attributes.h | 11 +++++++++++ lib/debugobjects.c | 2 +- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index 523355e91a2c..2aeb774ab024 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -777,21 +777,27 @@ struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns = { static struct io_pgtable_cfg *cfg_cookie __initdata; -static void __init dummy_tlb_flush_all(void *cookie) +/* + * __noipa prevents gcc from turning indirect iommu_flush_ops calls + * into direct calls from a specialized __arm_v7s_unmap() that triggers + * a build time section mismatch assertion. + */ +static __noipa void __init dummy_tlb_flush_all(void *cookie) { WARN_ON(cookie != cfg_cookie); } -static void __init dummy_tlb_flush(unsigned long iova, size_t size, - size_t granule, void *cookie) +static __noipa void __init dummy_tlb_flush(unsigned long iova, size_t size, + size_t granule, void *cookie) { WARN_ON(cookie != cfg_cookie); WARN_ON(!(size & cfg_cookie->pgsize_bitmap)); } -static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, - unsigned long iova, size_t granule, - void *cookie) +static __noipa void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather, + unsigned long iova, + size_t granule, + void *cookie) { dummy_tlb_flush(iova, granule, granule, cookie); } diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h index c16d4199bf92..836a50f5917a 100644 --- a/include/linux/compiler_attributes.h +++ b/include/linux/compiler_attributes.h @@ -396,6 +396,17 @@ # define __disable_sanitizer_instrumentation #endif +/* + * Optional: not supported by clang + * + * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-noipa + */ +#if __has_attribute(noipa) +# define __noipa __attribute__((noipa)) +#else +# define __noipa +#endif + /* * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-weak-function-attribute * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-weak-variable-attribute diff --git a/lib/debugobjects.c b/lib/debugobjects.c index b3151679d0d3..e4b7f77ece3b 100644 --- a/lib/debugobjects.c +++ b/lib/debugobjects.c @@ -1212,7 +1212,7 @@ struct self_test { static __initconst const struct debug_obj_descr descr_type_test; -static bool __init is_static_object(void *addr) +static __noipa bool __init is_static_object(void *addr) { struct self_test *obj = addr; From 22d41b176b9989efd21c3b2d3abf6728f05b9d9a Mon Sep 17 00:00:00 2001 From: Ashutosh Desai Date: Tue, 5 May 2026 17:07:12 +0000 Subject: [PATCH 576/697] nfc: hci: fix out-of-bounds read in HCP header parsing commit f040e590c035bfd9553fe79ee9585caf1b14d67b upstream. Both nfc_hci_recv_from_llc() and nci_hci_data_received_cb() read packet->header from skb->data at function entry without first checking that the buffer holds at least one byte. A malicious NFC peer can send a 0-byte HCP frame that passes through the SHDLC layer and reaches these functions, causing an out-of-bounds heap read of packet->header. The same 0-byte frame, if queued as a non-final fragment, also causes the reassembly loop to underflow msg_len to UINT_MAX, triggering skb_over_panic() when the reassembled skb is written. Fix this by adding a pskb_may_pull() check at the entry of each function before packet->header is first accessed. The existing pskb_may_pull() checks before the reassembled hcp_skb is cast to struct hcp_packet remain in place to guard the 2-byte HCP message header. Fixes: 8b8d2e08bf0d ("NFC: HCI support") Fixes: 11f54f228643 ("NFC: nci: Add HCI over NCI protocol support") Cc: stable@vger.kernel.org Reviewed-by: Simon Horman Signed-off-by: Ashutosh Desai Link: https://patch.msgid.link/20260505170712.96560-1-ashutoshdesai993@gmail.com Signed-off-by: David Heidelberg Signed-off-by: Greg Kroah-Hartman --- net/nfc/hci/core.c | 10 ++++++++++ net/nfc/nci/hci.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index 8618d57c23da..a8418528ca2b 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -861,6 +861,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb) struct sk_buff *frag_skb; int msg_len; + if (!pskb_may_pull(skb, NFC_HCI_HCP_PACKET_HEADER_LEN)) { + kfree_skb(skb); + return; + } + packet = (struct hcp_packet *)skb->data; if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) { skb_queue_tail(&hdev->rx_hcp_frags, skb); @@ -904,6 +909,11 @@ static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb) * unblock waiting cmd context. Otherwise, enqueue to dispatch * in separate context where handler can also execute command. */ + if (!pskb_may_pull(hcp_skb, NFC_HCI_HCP_HEADER_LEN)) { + kfree_skb(hcp_skb); + return; + } + packet = (struct hcp_packet *)hcp_skb->data; type = HCP_MSG_GET_TYPE(packet->message.header); if (type == NFC_HCI_HCP_RESPONSE) { diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c index 082ab66f120b..7f3b4fffb3d6 100644 --- a/net/nfc/nci/hci.c +++ b/net/nfc/nci/hci.c @@ -439,6 +439,11 @@ void nci_hci_data_received_cb(void *context, return; } + if (!pskb_may_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN)) { + kfree_skb(skb); + return; + } + packet = (struct nci_hcp_packet *)skb->data; if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) { skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb); @@ -482,6 +487,11 @@ void nci_hci_data_received_cb(void *context, * unblock waiting cmd context. Otherwise, enqueue to dispatch * in separate context where handler can also execute command. */ + if (!pskb_may_pull(hcp_skb, NCI_HCI_HCP_HEADER_LEN)) { + kfree_skb(hcp_skb); + return; + } + packet = (struct nci_hcp_packet *)hcp_skb->data; type = NCI_HCP_MSG_GET_TYPE(packet->message.header); if (type == NCI_HCI_HCP_RESPONSE) { From 448bb92ca101dde8a6e88b4dc824044b4e341604 Mon Sep 17 00:00:00 2001 From: Maoyi Xie Date: Mon, 4 May 2026 22:27:36 +0800 Subject: [PATCH 577/697] xfrm: route MIGRATE notifications to caller's netns commit 7e2a4f7ca0952820731ef7bdadfc9a9e9d3571b4 upstream. xfrm_send_migrate() in net/xfrm/xfrm_user.c and pfkey_send_migrate() in net/key/af_key.c both hardcode &init_net for the multicast that announces a successful XFRM_MSG_MIGRATE / SADB_X_MIGRATE. XFRM_MSG_MIGRATE arrives on a per-netns NETLINK_XFRM socket, and the rest of the xfrm/af_key netlink path was made netns-aware in 2008. The other 14 multicast paths in xfrm_user.c route their event using xs_net(x), xp_net(xp) or sock_net(skb->sk); only the migrate path was missed. Two consequences of the init_net hardcoding: 1. The notification (selector, old/new endpoint addresses, and the km_address) is delivered to listeners on init_net's XFRMNLGRP_MIGRATE / pfkey BROADCAST_ALL groups rather than on the issuing netns. An IKE daemon running in init_net therefore receives migration notifications originating from any other netns on the host. 2. An IKE daemon running inside a non-init netns and subscribed to its own XFRMNLGRP_MIGRATE / pfkey groups never receives the notification of its own migration. IKEv2 MOBIKE / address-update handling inside a netns is silently broken. Thread struct net through km_migrate() and the xfrm_mgr.migrate function pointer, drop the &init_net override in xfrm_send_migrate() and pfkey_send_migrate(), and pass the caller's net (already in scope in xfrm_migrate() via sock_net(skb->sk)) all the way down. struct xfrm_mgr is in-tree only and not exported as a stable API, so the function-pointer signature change is internal. pfkey_broadcast() is already netns-aware via net_generic(net, pfkey_net_id) since the pernet conversion. The five other pfkey_broadcast() callers in af_key.c already pass xs_net(x), sock_net(sk) or a per-netns net, so this only removes the &init_net outlier. Fixes: 5c79de6e79cd ("[XFRM]: User interface for handling XFRM_MSG_MIGRATE") Cc: stable@vger.kernel.org # v5.15+ Signed-off-by: Maoyi Xie Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- include/net/xfrm.h | 3 ++- net/key/af_key.c | 6 +++--- net/xfrm/xfrm_policy.c | 2 +- net/xfrm/xfrm_state.c | 4 ++-- net/xfrm/xfrm_user.c | 5 ++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 0a14daaa5dd4..7ad342e20750 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -715,6 +715,7 @@ struct xfrm_mgr { const struct xfrm_migrate *m, int num_bundles, const struct xfrm_kmaddress *k, + struct net *net, const struct xfrm_encap_tmpl *encap); bool (*is_alive)(const struct km_event *c); }; @@ -1891,7 +1892,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); #ifdef CONFIG_XFRM_MIGRATE int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_bundles, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap); struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m, struct net *net, u32 if_id); diff --git a/net/key/af_key.c b/net/key/af_key.c index e01939ab8103..96fdeadf46e3 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -3564,7 +3564,7 @@ static int set_ipsecrequest(struct sk_buff *skb, #ifdef CONFIG_NET_KEY_MIGRATE static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_bundles, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap) { int i; @@ -3669,7 +3669,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* broadcast migrate message to sockets */ - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net); + pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, net); return 0; @@ -3680,7 +3680,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, #else static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_bundles, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap) { return -ENOPROTOOPT; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 96e9262c4b48..ee1f6d5c391d 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -4704,7 +4704,7 @@ int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* Stage 5 - announce */ - km_migrate(sel, dir, type, m, num_migrate, k, encap); + km_migrate(sel, dir, type, m, num_migrate, k, net, encap); xfrm_pol_put(pol); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 1b81e92e3eee..898cddff498c 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -2836,7 +2836,7 @@ EXPORT_SYMBOL(km_policy_expired); #ifdef CONFIG_XFRM_MIGRATE int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_migrate, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap) { int err = -EINVAL; @@ -2847,7 +2847,7 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, list_for_each_entry_rcu(km, &xfrm_km_list, list) { if (km->migrate) { ret = km->migrate(sel, dir, type, m, num_migrate, k, - encap); + net, encap); if (!ret) err = ret; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 73d23ccdb5e2..2c11866be578 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -3262,10 +3262,9 @@ static int build_migrate(struct sk_buff *skb, const struct xfrm_migrate *m, static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_migrate, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap) { - struct net *net = &init_net; struct sk_buff *skb; int err; @@ -3283,7 +3282,7 @@ static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, #else static int xfrm_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, const struct xfrm_migrate *m, int num_migrate, - const struct xfrm_kmaddress *k, + const struct xfrm_kmaddress *k, struct net *net, const struct xfrm_encap_tmpl *encap) { return -ENOPROTOOPT; From dc6dcba80d72a27ab61831ad3d253316e0c9b9d5 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Wed, 6 May 2026 21:23:28 +0800 Subject: [PATCH 578/697] xfrm: ipcomp: Free destination pages on acomp errors commit 7dbac7680eb629b3b4dc7e98c34f943b8814c0c8 upstream. Move the out_free_req label up by a couple of lines so that the allocated dst SG list gets freed on error as well as success. Fixes: eb2953d26971 ("xfrm: ipcomp: Use crypto_acomp interface") Cc: stable@kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Xin Liu Reported-by: Yilin Zhu Signed-off-by: Herbert Xu Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_ipcomp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c index 43fdc6ed8dd1..51a0bc8ddac1 100644 --- a/net/xfrm/xfrm_ipcomp.c +++ b/net/xfrm/xfrm_ipcomp.c @@ -51,11 +51,15 @@ static int ipcomp_post_acomp(struct sk_buff *skb, int err, int hlen) struct scatterlist *dsg; int len, dlen; - if (unlikely(err)) - goto out_free_req; + if (unlikely(!req)) + return err; extra = acomp_request_extra(req); dsg = extra->sg; + + if (unlikely(err)) + goto out_free_req; + dlen = req->dlen; pskb_trim_unique(skb, 0); @@ -84,10 +88,10 @@ static int ipcomp_post_acomp(struct sk_buff *skb, int err, int hlen) skb_shinfo(skb)->nr_frags++; } while ((dlen -= len)); - for (; dsg; dsg = sg_next(dsg)) +out_free_req: + for (; dsg && sg_page(dsg); dsg = sg_next(dsg)) __free_page(sg_page(dsg)); -out_free_req: acomp_request_free(req); return err; } From aa0c7e59192b158c28525d065d343ebc7fe65c18 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 15 May 2026 11:45:31 -0400 Subject: [PATCH 579/697] xfrm: ah: use skb_to_full_sk in async output callbacks commit 79d8be262377f7112cfa3088dfc4142d5a2533f3 upstream. When AH output is offloaded to an asynchronous crypto provider (hardware accelerators such as AMD CCP, or a forced-async software shim used for testing), the digest completion fires ah_output_done() / ah6_output_done() on a workqueue. The egress skb at that point may have been originated by a TCP listener sending a SYN-ACK, which sets skb->sk to a request_sock via skb_set_owner_edemux(); it may also have been originated by an inet_timewait_sock retransmit. Neither is a full struct sock, and passing the raw skb->sk to xfrm_output_resume() then forwards a non-full socket through the rest of the xfrm output chain. xfrm_output_resume() and its downstream consumers expect a full sk where they dereference at all. The natural egress path through ah_output_done() does not crash today because the consumers that read past sock_common are either gated by sk_fullsock() or short-circuit on flags that are clear on a fresh request_sock; an exhaustive walk of the 50 most plausible consumers under sch_fq, dev_queue_xmit, netfilter, tc-egress and cgroup-egress BPF found no current unguarded deref. The bug is still a real type confusion that future consumer changes could turn into a memory-corruption primitive. This is the same bug class fixed for ESP in commit 1620c88887b1 ("xfrm: Fix the usage of skb->sk"). Apply the analogous fix to AH: convert skb->sk to a full socket pointer (or NULL) via skb_to_full_sk() before handing it to xfrm_output_resume(). The same async AH callbacks were touched recently for an independent ESN-related ICV layout bug in commit ec54093e6a8f ("xfrm: ah: account for ESN high bits in async callbacks"); the sk type-confusion addressed here is orthogonal. This patch is part of an ongoing audit of the AH callback paths; an ah_output ihl-validation hardening series is also currently under review on netdev. Reproduced under UML + KASAN + lockdep with a forced-async hmac(sha1) shim that registers at priority 9999 and wraps the sync in-tree hmac-sha1-lib. With the shim loaded, ah_output_done runs on every SYN-ACK egress through a transport-mode AH SA and skb->sk arrives as a request_sock (TCP_NEW_SYN_RECV); after this patch, xfrm_output_resume() receives the listener (the result of sk_to_full_sk()) and consumer derefs land on full-sock fields as intended. Fixes: 9ab1265d5231 ("xfrm: Use actual socket sk instead of skb socket for xfrm_output_resume") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/ipv4/ah4.c | 2 +- net/ipv6/ah6.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 8b0f15abbb38..7976f5106af0 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -143,7 +143,7 @@ static void ah_output_done(void *data, int err) } kfree(AH_SKB_CB(skb)->tmp); - xfrm_output_resume(skb->sk, skb, err); + xfrm_output_resume(skb_to_full_sk(skb), skb, err); } static int ah_output(struct xfrm_state *x, struct sk_buff *skb) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index bd9ec8000f0b..bf4e11614af2 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -337,7 +337,7 @@ static void ah6_output_done(void *data, int err) ah6_restore_hdrs(top_iph, iph_ext, extlen); kfree(AH_SKB_CB(skb)->tmp); - xfrm_output_resume(skb->sk, skb, err); + xfrm_output_resume(skb_to_full_sk(skb), skb, err); } static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) From befcb15c1f05e87d3072d68a02b5b9c80322cfb9 Mon Sep 17 00:00:00 2001 From: "Geoffrey D. Bennett" Date: Sun, 24 May 2026 06:34:14 +0930 Subject: [PATCH 580/697] ALSA: scarlett2: Fix 2i2 Gen 4 direct monitor gain on firmware 2417 commit db37cf47b67e38ade40de5cd74a4d4d772ff1416 upstream. Firmware 2417 for the Scarlett 4th Gen 2i2 moved the direct monitor gain parameter by 4 bytes, from offset 0x2a0 to 0x2a4, breaking the "Direct Monitor X Mix Y" controls. Special-case the offset in the get/set config helpers when the running firmware is 2417 or later. Fixes: 4e809a299677 ("ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4") Cc: Signed-off-by: Geoffrey D. Bennett Link: https://patch.msgid.link/ahIWTueUlWA5xiV+@m.b4.vu Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/usb/mixer_scarlett2.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c index da3c2741ef57..a31f255593d9 100644 --- a/sound/usb/mixer_scarlett2.c +++ b/sound/usb/mixer_scarlett2.c @@ -2504,6 +2504,27 @@ static int scarlett2_has_config_item( return !!private->config_set->items[config_item_num].offset; } +/* Return the configuration item's offset, applying any per-firmware + * overrides. + * + * Firmware 2417 for the 2i2 Gen 4 moved DIRECT_MONITOR_GAIN by 4 + * bytes. Apply that shift here so that the rest of the driver can + * keep using the single config set. This override can be removed + * once the multi-config-set framework lands. + */ +static int scarlett2_config_item_offset( + struct scarlett2_data *private, int config_item_num) +{ + int offset = private->config_set->items[config_item_num].offset; + + if (config_item_num == SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN && + private->info == &s2i2_gen4_info && + private->firmware_version >= 2417) + offset = 0x2a4; + + return offset; +} + /* Send a USB message to get configuration parameters; result placed in *buf */ static int scarlett2_usb_get_config( struct usb_mixer_interface *mixer, @@ -2513,6 +2534,7 @@ static int scarlett2_usb_get_config( const struct scarlett2_config *config_item = &private->config_set->items[config_item_num]; int size, err, i; + int item_offset; u8 *buf_8; u8 value; @@ -2522,13 +2544,15 @@ static int scarlett2_usb_get_config( if (!config_item->offset) return -EFAULT; + item_offset = scarlett2_config_item_offset(private, config_item_num); + /* Writes to the parameter buffer are always 1 byte */ size = config_item->size ? config_item->size : 8; /* For byte-sized parameters, retrieve directly into buf */ if (size >= 8) { size = size / 8 * count; - err = scarlett2_usb_get(mixer, config_item->offset, buf, size); + err = scarlett2_usb_get(mixer, item_offset, buf, size); if (err < 0) return err; if (config_item->size == 16) { @@ -2546,7 +2570,7 @@ static int scarlett2_usb_get_config( } /* For bit-sized parameters, retrieve into value */ - err = scarlett2_usb_get(mixer, config_item->offset, &value, 1); + err = scarlett2_usb_get(mixer, item_offset, &value, 1); if (err < 0) return err; @@ -2696,7 +2720,8 @@ static int scarlett2_usb_set_config( */ if (config_item->size >= 8) { size = config_item->size / 8; - offset = config_item->offset + index * size; + offset = scarlett2_config_item_offset(private, config_item_num) + + index * size; /* If updating a bit, retrieve the old value, set/clear the * bit as needed, and update value @@ -2705,7 +2730,7 @@ static int scarlett2_usb_set_config( u8 tmp; size = 1; - offset = config_item->offset; + offset = scarlett2_config_item_offset(private, config_item_num); err = scarlett2_usb_get(mixer, offset, &tmp, 1); if (err < 0) From b9027ff112b693b2e0a63d73626ce360720edc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A1ssio=20Gabriel?= Date: Thu, 21 May 2026 08:01:23 -0300 Subject: [PATCH 581/697] ALSA: firewire-motu: Protect register DSP event queue positions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 98fb1c1bb11e29eb609b7200a25e136e05aa4498 upstream. The register DSP event queue is updated under parser->lock, but snd_motu_register_dsp_message_parser_count_event() reads pull_pos and push_pos without the lock. snd_motu_register_dsp_message_parser_copy_event() also reads both queue positions before taking the lock. Protect these accesses with parser->lock as well. This keeps the hwdep poll/read path consistent with the producer side and with the cached meter/parameter accessors. Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") Cc: stable@vger.kernel.org Signed-off-by: Cássio Gabriel Reviewed-by: Takashi Sakamoto Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/20260521-alsa-firewire-motu-event-locking-v1-1-708e1c2b5e56@gmail.com Signed-off-by: Greg Kroah-Hartman --- .../firewire/motu/motu-register-dsp-message-parser.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c index a8053e3ef065..4ec23e6880d9 100644 --- a/sound/firewire/motu/motu-register-dsp-message-parser.c +++ b/sound/firewire/motu/motu-register-dsp-message-parser.c @@ -386,6 +386,8 @@ unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *m { struct msg_parser *parser = motu->message_parser; + guard(spinlock_irqsave)(&parser->lock); + if (parser->pull_pos > parser->push_pos) return EVENT_QUEUE_SIZE - parser->pull_pos + parser->push_pos; else @@ -395,13 +397,14 @@ unsigned int snd_motu_register_dsp_message_parser_count_event(struct snd_motu *m bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32 *event) { struct msg_parser *parser = motu->message_parser; - unsigned int pos = parser->pull_pos; - - if (pos == parser->push_pos) - return false; + unsigned int pos; guard(spinlock_irqsave)(&parser->lock); + if (parser->pull_pos == parser->push_pos) + return false; + + pos = parser->pull_pos; *event = parser->event_queue[pos]; ++pos; From b98ab51c45c5608a1c19ce7fd17a3032469bb83f Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Mon, 11 May 2026 10:43:14 -0400 Subject: [PATCH 582/697] netfilter: conntrack: tcp: do not force CLOSE on invalid-seq RST without direction check commit bed6e04be8e6b9133d8b16d5a42d0e0ce674fa9a upstream. An unintended behavior in the TCP conntrack state machine allows a connection to be forced into the CLOSE state using an RST packet with an invalid sequence number. Specifically, after a SYN packet is observed, an RST with an invalid SEQ can transition the conntrack entry to TCP_CONNTRACK_CLOSE, regardless of whether the RST corresponds to the expected reply direction. The relevant code path assumes the RST is a response to an outgoing SYN, but does not validate packet direction or ensure that a matching SYN was actually sent in the opposite direction. As a result, a crafted packet sequence consisting of a SYN followed by an invalid-sequence RST can prematurely terminate an active NAT entry. This makes connection teardown easier than intended. So, tighten the state transition logic to ensure that RST-triggered CLOSE transitions only occur when the RST is a valid response to a previously observed SYN in the correct direction. Cc: stable@vger.kernel.org Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.") Signed-off-by: Hamza Mahfooz Signed-off-by: Florian Westphal Signed-off-by: Greg Kroah-Hartman --- net/netfilter/nf_conntrack_proto_tcp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index b67426c2189b..e99ab1e88e9f 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1221,7 +1221,8 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, new_state = old_state; } if (((test_bit(IPS_SEEN_REPLY_BIT, &ct->status) - && ct->proto.tcp.last_index == TCP_SYN_SET) + && ct->proto.tcp.last_index == TCP_SYN_SET + && ct->proto.tcp.last_dir != dir) || (!test_bit(IPS_ASSURED_BIT, &ct->status) && ct->proto.tcp.last_index == TCP_ACK_SET)) && ntohl(th->ack_seq) == ct->proto.tcp.last_end) { From 35be14ea82982c2304b7d8a853957e0dd7537d77 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 18 May 2026 09:23:44 +0000 Subject: [PATCH 583/697] ASoC: qcom: q6asm-dai: close stream only when running commit 048c540ee76ded666bda74f9dae1ca3254e0633c upstream. q6asm_dai_close() and q6asm_dai_compr_free() currently issue CMD_CLOSE whenever prtd->state is non-zero. After prepare() closes an existing stream, the state is updated to Q6ASM_STREAM_STOPPED. Since this state is also non-zero, the close and free paths can send CMD_CLOSE again for a stream that has already been closed. Restrict CMD_CLOSE to the Q6ASM_STREAM_RUNNING state so the command is sent only when the ASM stream is still active. Fixes: 2a9e92d371db ("ASoC: qdsp6: q6asm: Add q6asm dai driver") Cc: Stable@vger.kernel.org Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260518092347.3446946-3-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/qcom/qdsp6/q6asm-dai.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index b1e26cc927f1..11e3202eaaae 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -447,12 +447,12 @@ static int q6asm_dai_close(struct snd_soc_component *component, struct q6asm_dai_rtd *prtd = runtime->private_data; if (prtd->audio_client) { - if (prtd->state) + if (prtd->state == Q6ASM_STREAM_RUNNING) { q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); - - q6asm_unmap_memory_regions(substream->stream, + q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); + } q6asm_audio_client_free(prtd->audio_client); prtd->audio_client = NULL; } @@ -669,7 +669,7 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = stream->private_data; if (prtd->audio_client) { - if (prtd->state) { + if (prtd->state == Q6ASM_STREAM_RUNNING) { q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); if (prtd->next_track_stream_id) { @@ -677,11 +677,11 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, prtd->next_track_stream_id, CMD_CLOSE); } - } - snd_dma_free_pages(&prtd->dma_buffer); - q6asm_unmap_memory_regions(stream->direction, + q6asm_unmap_memory_regions(stream->direction, prtd->audio_client); + } + snd_dma_free_pages(&prtd->dma_buffer); q6asm_audio_client_free(prtd->audio_client); prtd->audio_client = NULL; } From c4609fff0665e8ec0e198aa43a0934dfe338e0c8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 18 May 2026 09:23:43 +0000 Subject: [PATCH 584/697] ASoC: qcom: q6asm-dai: do not set stream state in event and trigger callbacks commit cee3e63e7106c3c81b2053371fdf14240bfba2fc upstream. The q6asm-dai stream state is used by prepare() to decide whether an existing stream setup needs to be closed before opening/configuring a new one. Updating the state from trigger or asynchronous DSP callbacks can make that state stale or incorrect relative to the actual setup lifetime. In particular, setting Q6ASM_STREAM_STOPPED on STOP or EOS completion can make prepare() believe there is no active setup to close, which can result in opening/configuring the same stream more than once. Keep stream state updates tied to prepare(), where the stream is actually closed and reopened, and stop changing it from trigger and EOS callbacks. Fixes: bfbb12dfa144 ("ASoC: qcom: q6asm-dai: perform correct state check before closing") Cc: Stable@vger.kernel.org Closes: https://lore.kernel.org/all/afS7rTHdc9TyIeLx@rdacayan/ Signed-off-by: Srinivas Kandagatla Link: https://patch.msgid.link/20260518092347.3446946-2-srinivas.kandagatla@oss.qualcomm.com Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/qcom/qdsp6/q6asm-dai.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 11e3202eaaae..bf093393bbad 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -187,7 +187,6 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_count, 0, 0, 0); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: - prtd->state = Q6ASM_STREAM_STOPPED; break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: { prtd->pcm_irq_pos += prtd->pcm_count; @@ -334,7 +333,6 @@ static int q6asm_dai_trigger(struct snd_soc_component *component, 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: - prtd->state = Q6ASM_STREAM_STOPPED; ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, CMD_EOS); break; @@ -545,8 +543,6 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, snd_compr_drain_notify(prtd->cstream); prtd->notify_on_drain = false; - } else { - prtd->state = Q6ASM_STREAM_STOPPED; } spin_unlock_irqrestore(&prtd->lock, flags); break; @@ -1009,7 +1005,6 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: - prtd->state = Q6ASM_STREAM_STOPPED; ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, CMD_EOS); break; From c093468aea8277f77272a4f199b2e15e19cabb59 Mon Sep 17 00:00:00 2001 From: Jingguo Tan Date: Mon, 18 May 2026 17:06:48 +0800 Subject: [PATCH 585/697] xfrm: esp: restore combined single-frag length gate commit dfa0d7b0ff1eb6b2c416b8fdb9b4f2cefba57a40 upstream. The ESP out-of-place fast path appends the trailer in esp_output_head() before esp_output_tail() allocates the destination page frag. The head-side gate currently checks skb->data_len and tailen separately, but the tail code allocates a single destination frag from the combined post-trailer skb->data_len. Reject the page-frag fast path when the combined aligned length exceeds a page. Otherwise skb_page_frag_refill() may fall back to a single page while the destination sg still spans the combined skb->data_len. Restore this combined-length page gate for both IPv4 and IPv6. Fixes: 5bd8baab087d ("esp: limit skb_page_frag_refill use to a single page") Cc: stable@vger.kernel.org Signed-off-by: Lin Ma Signed-off-by: Chenyuan Mi Signed-off-by: Jingguo Tan Reviewed-by: Sabrina Dubroca Signed-off-by: Steffen Klassert Signed-off-by: Greg Kroah-Hartman --- net/ipv4/esp4.c | 4 ++-- net/ipv6/esp6.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 6a5febbdbee4..513c8215c947 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -419,8 +419,8 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * return err; } - if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE || - ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE) + if (ALIGN(skb->data_len + tailen, L1_CACHE_BYTES) > + PAGE_SIZE) goto cow; if (!skb_cloned(skb)) { diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 9c06c5a1419d..57481e423e59 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -448,8 +448,8 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info return err; } - if (ALIGN(tailen, L1_CACHE_BYTES) > PAGE_SIZE || - ALIGN(skb->data_len, L1_CACHE_BYTES) > PAGE_SIZE) + if (ALIGN(skb->data_len + tailen, L1_CACHE_BYTES) > + PAGE_SIZE) goto cow; if (!skb_cloned(skb)) { From 2ffd8b0dd4484b92325d3f7e098cdb66adff2ca2 Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 26 May 2026 09:36:11 +0800 Subject: [PATCH 586/697] ALSA: hda/realtek: Fix speaker output on ASUS ROG Strix G615LP commit 20587302f8d700f26ee2c8a60ffb0a69ae0edf16 upstream. Add quirk for ALC294 codec on ASUS ROG Strix G615LP (SSID 1043:1214) using ALC287_FIXUP_TXNW2781_I2C_ASUS to fix speaker output. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221173 Cc: Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260526013611.1954949-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/hda/codecs/realtek/alc269.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 4cd5b719556e..c18c18f9e652 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7053,6 +7053,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), HDA_CODEC_QUIRK(0x1043, 0x1204, "ASUS Strix G16 G615JMR", ALC287_FIXUP_TXNW2781_I2C_ASUS), SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C), + HDA_CODEC_QUIRK(0x1043, 0x1214, "ASUS ROG Strix G615LP", ALC287_FIXUP_TXNW2781_I2C_ASUS), SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE), From 3d63b8077f5bd177c8f9c852a2e095dd8b52c452 Mon Sep 17 00:00:00 2001 From: Qbeliw Tanaka Date: Thu, 30 Apr 2026 21:44:12 -0700 Subject: [PATCH 587/697] Input: xpad - add "Nova 2 Lite" from GameSir commit 1f6ac0f8441c48c4cc250141e1da8486c13512ba upstream. Add support for the gamepad "Nova 2 Lite" from GameSir, compatible with the Xbox 360 gamepad. Signed-off-by: Qbeliw Tanaka Link: https://patch.msgid.link/20260429.162040.930225048583399359.q.tanaka@gmx.com Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/xpad.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index c75e1b695dfc..e8251f3dd339 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -425,6 +425,7 @@ static const struct xpad_device { { 0x3285, 0x0662, "Nacon Revolution5 Pro", 0, XTYPE_XBOX360 }, { 0x3285, 0x0663, "Nacon Evol-X", 0, XTYPE_XBOXONE }, { 0x3537, 0x1004, "GameSir T4 Kaleid", 0, XTYPE_XBOX360 }, + { 0x3537, 0x100f, "GameSir Nova 2 Lite", 0, XTYPE_XBOX360 }, { 0x3537, 0x1010, "GameSir G7 SE", 0, XTYPE_XBOXONE }, { 0x3651, 0x1000, "CRKD SG", 0, XTYPE_XBOX360 }, { 0x366c, 0x0005, "ByoWave Proteus Controller", MAP_SHARE_BUTTON, XTYPE_XBOXONE, FLAG_DELAY_INIT }, From 6617ee91062bc2099a37da8e1404e5d183058928 Mon Sep 17 00:00:00 2001 From: Dmitriy Zharov Date: Thu, 30 Apr 2026 22:35:22 +0400 Subject: [PATCH 588/697] Input: xpad - add support for ASUS ROG RAIKIRI II commit c897cf120696b94f56ed0f3197ba9a77071a59ec upstream. Add the VID/PIDs for the ASUS ROG RAIKIRI II controller to xpad_device and the VID to xpad_table. The controller has a physical PC/XBOX toggle which switches between XBOX360 and XBOXONE protocols. Signed-off-by: Dmitriy Zharov Link: https://patch.msgid.link/20260430183522.122151-1-contact@zharov.dev Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/joystick/xpad.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index e8251f3dd339..ebfb841aafee 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -220,6 +220,10 @@ static const struct xpad_device { { 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 }, { 0x0b05, 0x1a38, "ASUS ROG RAIKIRI", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0b05, 0x1abb, "ASUS ROG RAIKIRI PRO", 0, XTYPE_XBOXONE }, + { 0x0b05, 0x1c91, "ASUS ROG RAIKIRI II", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c92, "ASUS ROG RAIKIRI II WIRELESS", 0, XTYPE_XBOX360 }, + { 0x0b05, 0x1c96, "ASUS ROG RAIKIRI II XBOX", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, + { 0x0b05, 0x1d04, "ASUS ROG RAIKIRI II XBOX WIRELESS", MAP_SHARE_BUTTON, XTYPE_XBOXONE }, { 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX }, { 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX }, { 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX }, @@ -542,6 +546,7 @@ static const struct usb_device_id xpad_table[] = { { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz Gamepad */ + XPAD_XBOX360_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOXONE_VENDOR(0x0b05), /* ASUS controllers */ XPAD_XBOX360_VENDOR(0x0c12), /* Zeroplus X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x0db0), /* Micro Star International X-Box 360 controllers */ From 94215d55b09445993929f4fc966061d61de74929 Mon Sep 17 00:00:00 2001 From: Ali Ganiyev Date: Mon, 25 May 2026 10:23:47 +0900 Subject: [PATCH 589/697] ksmbd: OOB read regression in smb_check_perm_dacl() ACE-walk loops commit 0e60dafe97eca61721f3db456f97d97a80c6c8ae upstream. Commit d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") introduced a transposed bounds check: if (offsetof(struct smb_ace, sid) + aces_size < CIFS_SID_BASE_SIZE) Since offsetof(..sid) is 8 and CIFS_SID_BASE_SIZE is 8, this evaluates to `aces_size < 0`. Because `aces_size` is always non-negative, this check becomes dead code and never breaks the loop. Worse, that commit removed the old 4-byte guard, meaning the loop now reads `ace->size` (offset 2) even when `aces_size` is 0-3 bytes. This re-opens a 2-byte heap out-of-bounds (OOB) read past the pntsd allocation during subsequent SMB2_CREATE operations. Fix this by properly transposing the comparison to require at least 16 bytes (8-byte offset + 8-byte SID base), matching the correct form used in smb_inherit_dacl(). Fixes: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") Cc: stable@vger.kernel.org Signed-off-by: Ali Ganiyev Acked-by: Namjae Jeon Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/smb/server/smbacl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index 6c4f9c8c7f13..e3c512675c63 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1446,8 +1446,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, sid) + - aces_size < CIFS_SID_BASE_SIZE) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); if (ace_size > aces_size || @@ -1470,8 +1470,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path, ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); aces_size = acl_size - sizeof(struct smb_acl); for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { - if (offsetof(struct smb_ace, sid) + - aces_size < CIFS_SID_BASE_SIZE) + if (aces_size < offsetof(struct smb_ace, sid) + + CIFS_SID_BASE_SIZE) break; ace_size = le16_to_cpu(ace->size); if (ace_size > aces_size || From 639fa8af506ec96cd1292d580f598e93a1939c88 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Mon, 18 May 2026 15:34:05 +0800 Subject: [PATCH 590/697] misc: rp1: Send IACK on IRQ activate to fix kdump/kexec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 36770417153644bc88281c7284730ef1d14d8d3c upstream. After a kexec/kdump reboot, the macb Ethernet controller fails to receive any packets, causing DHCP to hang indefinitely and the network interface to be unusable despite link being up. The root cause is that RP1's level-triggered MSI-X interrupt sources (such as macb on hwirq 6) may have their internal state machines stuck in the "waiting for IACK" state. This happens because the previous kernel crashed before sending the acknowledgment for a pending level interrupt. In this stuck state, RP1 will not generate new MSI-X writes even though the interrupt source remains asserted. Since no new MSI-X is sent, the GIC never sees a new edge, the chained IRQ handler is never invoked, and the interrupt is permanently lost. Fix this by sending MSIX_CFG_IACK in rp1_irq_activate(). This unconditionally resets the MSI-X state machine back to idle when a child device requests its interrupt. If the interrupt source is still asserted, RP1 will immediately issue a new MSI-X with the freshly configured msg_addr/msg_data, and normal interrupt delivery resumes. Writing IACK when the state machine is already idle (i.e., on a normal cold boot) is harmless — it has no effect. Fixes: 49d63971f963 ("misc: rp1: RaspberryPi RP1 misc driver") Cc: stable Signed-off-by: Xiaolei Wang Link: https://patch.msgid.link/20260518073405.2115003-1-xiaolei.wang@windriver.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/misc/rp1/rp1_pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/rp1/rp1_pci.c b/drivers/misc/rp1/rp1_pci.c index a342bcc6164b..4d00dab48d47 100644 --- a/drivers/misc/rp1/rp1_pci.c +++ b/drivers/misc/rp1/rp1_pci.c @@ -148,6 +148,7 @@ static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd, struct rp1_dev *rp1 = d->host_data; msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE); + msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_IACK); return 0; } From 7f95f4792c0dc767fcb8e405391e779ab419d55a Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 May 2026 11:54:45 -0700 Subject: [PATCH 591/697] Input: atmel_mxt_ts - fix boundary check in mxt_prepare_cfg_mem commit baa0210fb6a9dc3882509a9411b6d284d88fe30e upstream. When a configuration file provides an object size that is larger than the driver's known mxt_obj_size(object), the driver intends to discard the extra bytes. The loop iterates using for (i = 0; i < size; i++). Inside the loop, the condition to skip processing extra bytes is: if (i > mxt_obj_size(object)) continue; Since i is a 0-based index, the valid indices for the object are 0 through mxt_obj_size(object) - 1. When i == mxt_obj_size(object), the condition evaluates to false, and the code processes the byte instead of discarding it. This causes the code to calculate byte_offset = reg + i - cfg->start_ofs and writes the byte there, overwriting exactly one byte of the adjacent instance or object. Update the boundary check to skip extra bytes correctly by using >=. Fixes: 50a77c658b80 ("Input: atmel_mxt_ts - download device config using firmware loader") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Reviewed-by: Ricardo Ribalda Link: https://patch.msgid.link/20260504185448.4055973-1-dmitry.torokhov@gmail.com Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index dd0544cc1bc1..15bac83ea92e 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1477,7 +1477,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) } cfg->raw_pos += offset; - if (i > mxt_obj_size(object)) + if (i >= mxt_obj_size(object)) continue; byte_offset = reg + i - cfg->start_ofs; From fdb74898d91d7871e85d752d33bb61d336301d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Bazaes?= Date: Wed, 13 May 2026 21:35:49 -0400 Subject: [PATCH 592/697] Input: synaptics - add LEN2058 to SMBus passlist for ThinkPad E490 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 16ca52bc209fa4bf9239cd9e5643e95533476b58 upstream. The Lenovo ThinkPad E490 (PNP ID: LEN2058) has a Synaptics TM3471-020 touchpad that supports SMBus/RMI4 mode but is not listed in smbus_pnp_ids[]. Without this entry, RMI4 over SMBus is not enabled by default, and the touchpad falls back to PS/2 mode. Adding LEN2058 to the passlist enables automatic RMI4 detection without requiring the psmouse.synaptics_intertouch parameter, and matches the behavior of similar ThinkPad models already in the list (E480/LEN2054, E580/LEN2055). Tested on ThinkPad E490 with kernel 7.0.5-zen1 and Arch Linux. RMI4 over SMBus is confirmed working without any kernel parameters. Signed-off-by: Nicolás Bazaes Assisted-by: Claude:claude-sonnet-4-6 Link: https://patch.msgid.link/20260514013552.14234-1-contacto@bazaes.cl Cc: stable@vger.kernel.org Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/mouse/synaptics.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index c5c88a75a019..4fbb58cc145d 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -190,6 +190,7 @@ static const char * const smbus_pnp_ids[] = { "LEN2044", /* L470 */ "LEN2054", /* E480 */ "LEN2055", /* E580 */ + "LEN2058", /* E490 */ "LEN2068", /* T14 Gen 1 */ "SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */ "SYN3003", /* HP EliteBook 850 G1 */ From 269f5be6a6e4a8dd511887181d83c4b1a1f34d57 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 22 Apr 2026 17:21:19 +0100 Subject: [PATCH 593/697] comedi: comedi_test: fix check for valid scan_begin_src in waveform_ai_cmdtest() commit 542f5248cb481073203e0dadab5bcbd28aeae308 upstream. Commit 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") neglected to add a test that `scan_begin_src` has only one bit set. The allowed values are `TRIG_FOLLOW` and `TRIG_TIMER`, but the code incorrectly also allows `TRIG_FOLLOW | TRIG_TIMER`. Add a call to `comedi_check_trigger_is_unique()` to check that only one trigger source bit is set. Fixes: 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260422162138.36003-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 7984950f0f99..2d2788251b7d 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -274,6 +274,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, /* Step 2a : make sure trigger sources are unique */ err |= comedi_check_trigger_is_unique(cmd->convert_src); + err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); err |= comedi_check_trigger_is_unique(cmd->stop_src); /* Step 2b : and mutually compatible */ From c7e670cb2538d7b3a1a1cdc87764b26464a72bdc Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 22 Apr 2026 15:46:37 +0100 Subject: [PATCH 594/697] comedi: comedi_test: Fix limiting of convert_arg in waveform_ai_cmdtest() commit 8a3bee801d420be8a7a0bae4a26547b353b8fe22 upstream. The function checks and possibly modifies the description of an asynchronous command to be run on the analog input subdevice of a comedi device attached to the "comedi_test" driver, returning 0 if no modifications were required, or a positive value that indicates which step of the checking process it failed on. Step 4 fixes up various argument values for various trigger sources. There are two bugs in the fixing up of the `convert_arg` value to keep the `scan_begin_arg` value within the range of `unsigned int` when `scan_begin_src` and `convert_src` both have the value `TRIG_TIMER`, which indicates that the corresponding `_arg` values hold a time period in nanoseconds. The code also uses `scan_end_arg` which hold the number of "conversions" within each "scan". The goal is to end up with the scan period being less than or equal to the convert period multiplied by the number of conversions per scan. It intends to do that by clamping the `convert_arg` value to a maximum value of `UINT_MAX / scan_end_arg` rounded down to a multiple of 1000 (`NSEC_PER_USEC`). (The rounding from nanoseconds to microseconds is because the driver is modelling a device that uses a 1 MHz clock for timing. This is partly because that is a more typical timing base for real hardware devices driven by comedi, and partly because the driver used to use `struct timeval` internally.) The first bug is that the code checks if `scan_begin_arg == TRIG_TIMER` when it should be checking if `scan_begin_src == TRIG_TIMER`. The bugged check will always fail because if `scan_begin_src == TRIG_TIMER`, then `scan_begin_arg` will be at least 1000 (`NSEC_PER_USEC`), otherwise `scan_begin_src == TRIG_FOLLOW` and `scan_begin_arg` will be 0. (N.B `TRIG_TIMER` is defined as `0x10`.) The second bug is that is rounding the maximum value down to a multiple of 1000000000 (`NSEC_PER_SEC`) instead of 1000 (`NSEC_PER_USEC`), however this bug is not reached due to the first bug. This patch fixes both bugs. Fixes: 783ddaebd397 ("staging: comedi: comedi_test: support scan_begin_src == TRIG_FOLLOW") Fixes: 5afdcad2f818 ("staging: comedi: comedi_test: limit maximum convert_arg") Cc: stable Signed-off-by: Ian Abbott Link: https://patch.msgid.link/20260422144637.27692-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index 2d2788251b7d..866c4a5fe04f 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -325,10 +325,10 @@ static int waveform_ai_cmdtest(struct comedi_device *dev, arg = min(arg, rounddown(UINT_MAX, (unsigned int)NSEC_PER_USEC)); arg = NSEC_PER_USEC * DIV_ROUND_CLOSEST(arg, NSEC_PER_USEC); - if (cmd->scan_begin_arg == TRIG_TIMER) { + if (cmd->scan_begin_src == TRIG_TIMER) { /* limit convert_arg to keep scan_begin_arg in range */ limit = UINT_MAX / cmd->scan_end_arg; - limit = rounddown(limit, (unsigned int)NSEC_PER_SEC); + limit = rounddown(limit, (unsigned int)NSEC_PER_USEC); arg = min(arg, limit); } err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); From b4bebb6e0a449ec1c621e1e7df682c402f386a1f Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 21:46:04 +0800 Subject: [PATCH 595/697] counter: Fix refcount leak in counter_alloc() error path commit d9eeb0ea0d2de658663bfaa9c26eccdd8fd64440 upstream. After device_initialize(), the lifetime of the embedded struct device is expected to be managed through the device core reference counting. In counter_alloc(), if dev_set_name() fails after device_initialize(), the error path removes the chrdev, frees the ID, and frees the backing allocation directly instead of releasing the device reference with put_device(). This bypasses the normal device lifetime rules and may leave the reference count of the embedded struct device unbalanced, resulting in a refcount leak. The issue was identified by a static analysis tool I developed and confirmed by manual review. Fix this by using put_device() in the dev_set_name() failure path and let counter_device_release() handle the final cleanup. Fixes: 4da08477ea1f ("counter: Set counter device name") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Link: https://lore.kernel.org/r/20260413134604.2861772-1-lgs201920130244@gmail.com Signed-off-by: William Breathitt Gray Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 50bd30ba3d03..0b1dac61b7b5 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -124,7 +124,8 @@ struct counter_device *counter_alloc(size_t sizeof_priv) err_dev_set_name: - counter_chrdev_remove(counter); + put_device(dev); + return NULL; err_chrdev_add: ida_free(&counter_ida, dev->id); From 66f8bfea055b23719b4fd6ce207c44de37d82a59 Mon Sep 17 00:00:00 2001 From: Zhaoyang Yu <2426767509@qq.com> Date: Thu, 9 Apr 2026 13:41:58 +0800 Subject: [PATCH 596/697] tty: serial: pch_uart: add check for dma_alloc_coherent() commit 6fe472c1bbbe238e91141f7cabc1226e96a60d43 upstream. Add a check for dma_alloc_coherent() failure to prevent a potential NULL pointer dereference in dma_handle_rx(). Properly release DMA channels and the PCI device reference using a goto ladder if the allocation fails. Fixes: 3c6a483275f4 ("Serial: EG20T: add PCH_UART driver") Cc: stable Signed-off-by: Zhaoyang Yu <2426767509@qq.com> Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/tencent_E328416B7CFD436F6029F2DF02AD7ED89C08@qq.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pch_uart.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 884fefbfd5a1..9992fa231e4e 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -689,8 +689,7 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n", __func__); - pci_dev_put(dma_dev); - return; + goto err_pci_get; } priv->chan_tx = chan; @@ -704,18 +703,26 @@ static void pch_request_dma(struct uart_port *port) if (!chan) { dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n", __func__); - dma_release_channel(priv->chan_tx); - priv->chan_tx = NULL; - pci_dev_put(dma_dev); - return; + goto err_req_tx; } /* Get Consistent memory for DMA */ priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, &priv->rx_buf_dma, GFP_KERNEL); + if (!priv->rx_buf_virt) + goto err_req_rx; priv->chan_rx = chan; pci_dev_put(dma_dev); + return; + +err_req_rx: + dma_release_channel(chan); +err_req_tx: + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; +err_pci_get: + pci_dev_put(dma_dev); } static void pch_dma_rx_complete(void *arg) From 9fd48937046efc9abb89379d63ee9cc5c661d711 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 15 May 2026 12:41:21 +0000 Subject: [PATCH 597/697] tty: serial: samsung: Remove redundant port lock acquisition in rx helpers commit a3bb136bff5e6a5e48cdd813246c9c4686feaaa9 upstream. Sashiko identified a deadlock when the console flow is engaged [1]. When console flow control is enabled (UPF_CONS_FLOW), s3c24xx_serial_stop_tx() calls s3c24xx_serial_rx_enable() and s3c24xx_serial_start_tx() calls s3c24xx_serial_rx_disable(). The serial core framework invokes the .stop_tx() and .start_tx() callbacks with the port->lock spinlock already held. Furthermore, all internal driver paths that invoke stop_tx (such as the DMA TX completion handler s3c24xx_serial_tx_dma_complete() or the PIO TX IRQ handler s3c24xx_serial_tx_irq()) also acquire port->lock prior to calling it. (Note that s3c24xx_serial_start_tx() is only invoked by the serial core). However, s3c24xx_serial_rx_enable() and s3c24xx_serial_rx_disable() unconditionally attempt to acquire port->lock again using uart_port_lock_irqsave(). Since spinlocks are not recursive, this causes a deadlock on the same CPU when console flow control is engaged. Remove the redundant lock acquisition from both rx helper functions. Cc: stable Fixes: b497549a035c ("[ARM] S3C24XX: Split serial driver into core and per-cpu drivers") Reported-by: John Ogness Closes: https://sashiko.dev/#/patchset/20260506121606.5805-1-john.ogness%40linutronix.de [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260515-samsung-tty-flow-control-deadlock-v1-1-93255edbc9bc@linaro.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung_tty.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 2fb58c626daf..7a0b89d856c2 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -245,12 +245,9 @@ static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port) static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; int count = 10000; u32 ucon, ufcon; - uart_port_lock_irqsave(port, &flags); - while (--count && !s3c24xx_serial_txempty_nofifo(port)) udelay(100); @@ -263,23 +260,18 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 1; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_rx_disable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned long flags; u32 ucon; - uart_port_lock_irqsave(port, &flags); - ucon = rd_regl(port, S3C2410_UCON); ucon &= ~S3C2410_UCON_RXIRQMODE; wr_regl(port, S3C2410_UCON, ucon); ourport->rx_enabled = 0; - uart_port_unlock_irqrestore(port, flags); } static void s3c24xx_serial_stop_tx(struct uart_port *port) From 7118304b1a776d915147f367da5b273ee38737ec Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 27 Apr 2026 15:57:55 +0800 Subject: [PATCH 598/697] usb: chipidea: core: convert ci_role_switch to local variable commit 8f6aa392653e52a45858cff5c063df550028836b upstream. When a system contains multiple USB controllers, the global ci_role_switch variable may be overwritten by subsequent driver initialization code. This can cause issues in the following cases: - The 2nd ci_hdrc_probe() sees ci_role_switch.fwnode as non-NULL even though the "usb-role-switch" property is not present for the controller. - When the ci_hdrc device is unbound and bound again, ci_role_switch fwnode will not be reassigned, and the old value will be used instead. Convert ci_role_switch to a local variable to fix these issues. Fixes: 05559f10ed79 ("usb: chipidea: add role switch class support") Cc: stable Acked-by: Peter Chen Reviewed-by: Frank Li Signed-off-by: Xu Yang Link: https://patch.msgid.link/20260427075755.3611217-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 6df442435af6..4d3a89b9d386 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -669,12 +669,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci) return role; } -static struct usb_role_switch_desc ci_role_switch = { - .set = ci_usb_role_switch_set, - .get = ci_usb_role_switch_get, - .allow_userspace_control = true, -}; - static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { @@ -801,9 +795,6 @@ static int ci_get_platdata(struct device *dev, cable->connected = false; } - if (device_property_read_bool(dev, "usb-role-switch")) - ci_role_switch.fwnode = dev->fwnode; - platdata->pctl = devm_pinctrl_get(dev); if (!IS_ERR(platdata->pctl)) { struct pinctrl_state *p; @@ -1045,6 +1036,7 @@ ATTRIBUTE_GROUPS(ci); static int ci_hdrc_probe(struct platform_device *pdev) { + struct usb_role_switch_desc ci_role_switch = {}; struct device *dev = &pdev->dev; struct ci_hdrc *ci; struct resource *res; @@ -1191,7 +1183,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci_role_switch.fwnode) { + if (device_property_read_bool(dev, "usb-role-switch")) { + ci_role_switch.set = ci_usb_role_switch_set; + ci_role_switch.get = ci_usb_role_switch_get; + ci_role_switch.allow_userspace_control = true; + ci_role_switch.fwnode = dev_fwnode(dev); ci_role_switch.driver_data = ci; ci->role_switch = usb_role_switch_register(dev, &ci_role_switch); From 3bc65566331abcf884ebcf09b1755a134914b2c9 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 18 May 2026 07:32:07 +0200 Subject: [PATCH 599/697] usb: core: Fix up Interrupt IN endpoints with bogus wBytesPerInterval commit 727d045d064b7c9a24db3bce9c0485a382cb768b upstream. Tao Xue found that some common devices violate USB 3.x section 9.6.7 by reporting wBytesPerInterval lower than the size of packets they actually send. I confirmed that AX88179 may set it to 0 and RTL8153 CDC configuration sets it to 8 but sends both 8 and 16 byte packets: S Ii:11:007:3 -115:128 16 < C Ii:11:007:3 0:128 8 = a1000000 01000000 S Ii:11:007:3 -115:128 16 < C Ii:11:007:3 0:128 16 = a12a0000 01000800 00000000 00000000 Most xHCI host controllers neglect interrupt bandwidth reservations and let such devices exceed theirs, some fail the URB with EOVERFLOW. Assume that wBytesPerInterval lower than wMaxPacketSize is bogus and increase it to the worst case maximum on interrupt IN endpoints. This solves xHCI problems and appears to have no other effect. Interrupt transfers are not limited to one interval and drivers submit URBs of class defined size without looking at wBytesPerInterval. Any multi- interval transfer is considered terminated by a packet shorter than wMaxPacketSize regardless of wBytesPerInterval - see USB3 8.10.3. Stay in spec on OUT endpoints and isochronous. No buggy devices are known and we don't want to risk sending more data than the device is prepared to handle or confusing isoc drivers regarding altsetting capacities guaranteed by the device itself. And don't complain when wMaxPacketSize <= wBytesPerInterval < wMaxPacketSize * (bMaxBurst+1) because enabling this seems to be the exact goal of the spec. Reported-and-tested-by: Tao Xue Closes: https://lore.kernel.org/linux-usb/20260402021400.28853-1-xuetao09@huawei.com/ Cc: stable@vger.kernel.org Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260518073207.5b7d26e7.michal.pecio@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 3067e18ec4d8..1c0f36c85bcf 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -191,7 +191,14 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, (desc->bMaxBurst + 1); else max_tx = 999999; - if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { + /* + * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite. + * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN + * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits. + */ + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx || + (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) && + usb_endpoint_is_int_in(&ep->desc))) { dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in " "config %d interface %d altsetting %d ep %d: " "setting to %d\n", From 69f9f2b30af03d9b6e83f78fb0f734b6066d4678 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Thu, 9 Apr 2026 10:11:04 +0000 Subject: [PATCH 600/697] usb: musb: omap2430: Fix use-after-free in omap2430_probe() commit e194ce048f5a6c549b3a23a8c568c6470f40f772 upstream. In omap2430_probe(), of_node_put(np) is called prematurely before the last access to np, leading to a use-after-free if the node's reference count drops to zero. Move the of_node_put() calls after the last use of np in both the success and error paths. Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources") Cc: stable Signed-off-by: Wentao Liang Link: https://patch.msgid.link/20260409101104.480623-1-vulab@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/omap2430.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index c35c07b7488c..aadabc415145 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -338,7 +338,6 @@ static int omap2430_probe(struct platform_device *pdev) } else { device_set_of_node_from_dev(&musb->dev, &pdev->dev); } - of_node_put(np); glue->dev = &pdev->dev; glue->musb = musb; @@ -456,6 +455,7 @@ static int omap2430_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err_disable_rpm; } + of_node_put(np); return 0; @@ -465,6 +465,7 @@ static int omap2430_probe(struct platform_device *pdev) if (!IS_ERR(glue->control_otghs)) put_device(glue->control_otghs); err_put_musb: + of_node_put(np); platform_device_put(musb); return ret; From 94b05aec1985ac35d136be7fef9ffdec0e1d159f Mon Sep 17 00:00:00 2001 From: "Stephen J. Fuhry" Date: Wed, 13 May 2026 13:14:19 -0400 Subject: [PATCH 601/697] USB: quirks: add NO_LPM for Lenovo ThinkPad USB-C Dock Gen2 hub controllers commit 9ddb9c0deca48d2c2a22ebf4d2f35c925a520328 upstream. The Lenovo ThinkPad USB-C Dock Gen2 (17ef:a391, 17ef:a392) hub controllers exhibit link instability when USB Link Power Management is enabled, similar to the dock's Ethernet adapter (17ef:a387) which already carries USB_QUIRK_NO_LPM. When the dock reconnects after a transient disconnect, the hub controllers enter LPM states between re-enumeration retries, causing repeated disconnect/reconnect cycles lasting up to two minutes. Disabling LPM for these devices restores stable enumeration. Signed-off-by: Stephen J. Fuhry Cc: stable Link: https://patch.msgid.link/20260513171419.44849-1-fuhrysteve@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 34b1f7df3529..21f9b7fec8cb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -514,6 +514,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM }, + /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */ + { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM }, + { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, From 5b78d8b9a83216da8fd6de442f77d52f4f3e622a Mon Sep 17 00:00:00 2001 From: Sam Burkels Date: Fri, 1 May 2026 15:23:46 +0200 Subject: [PATCH 602/697] usb: storage: Add quirks for PNY Elite Portable SSD commit b53ebb811e00be50a779ce4e7aee604178b4a825 upstream. The PNY Elite Portable SSD (USB ID 154b:f009) is a sibling of the already-quirked PNY Pro Elite SSDs (154b:f00b and 154b:f00d). Like its siblings, it uses a Phison-based USB-SATA bridge that exhibits firmware bugs when bound to the uas driver. Without quirks, the device fails to complete READ CAPACITY commands when accessed over UAS on a SuperSpeed (USB 3) port. The device enumerates and reports as a SCSI direct-access device, but reports zero logical blocks and never finishes spin-up: usb 2-3: new SuperSpeed USB device number 8 using xhci_hcd usb 2-3: New USB device found, idVendor=154b, idProduct=f009 usb 2-3: Product: PNY ELITE PSSD usb 2-3: Manufacturer: PNY scsi host0: uas scsi 0:0:0:0: Direct-Access PNY PNY ELITE PSSD 0 sd 0:0:0:0: [sda] Spinning up disk... [...10+ seconds of polling, no progress...] sd 0:0:0:0: [sda] Read Capacity(16) failed: hostbyte=DID_ERROR sd 0:0:0:0: [sda] Read Capacity(10) failed: hostbyte=DID_ERROR sd 0:0:0:0: [sda] 0 512-byte logical blocks: (0 B/0 B) Tested each individual quirk to find the minimum that fixes this: - US_FL_NO_ATA_1X alone: device hangs on spin-up - US_FL_NO_REPORT_OPCODES alone: works on USB 2.0, hangs on USB 3.0 - US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES: works on both With both quirks the device enumerates correctly while still using the uas driver, and delivers full UAS throughput (~281 MB/s sequential read on a USB 3.0 Gen 1 port). The existing PNY Pro Elite entries (f00b, f00d) only set NO_ATA_1X, but this device additionally chokes on REPORT OPCODES under SuperSpeed. Signed-off-by: Sam Burkels Acked-by: Oliver Neukum Cc: stable Link: https://patch.msgid.link/20260501132346.86572-1-sam@1a38.nl Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_uas.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 939a98c2d3f7..d6f86d5db3bf 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), +/* Reported-by: Sam Burkels */ +UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999, + "PNY", + "PNY ELITE PSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES), + /* Reported-by: Thinh Nguyen */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY", From 88d459e5b5a46da1ef9fd6f52d9439343edeec88 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 17 Apr 2026 12:35:52 -0400 Subject: [PATCH 603/697] usbip: vudc: Fix use after free bug in vudc_remove due to race condition commit d96209626a29ea64666be98c30b30ac82e5f1be6 upstream. This patch follows up Zheng Wang's 2023 report of a use-after-free in vudc_remove(). The original thread stalled on Shuah Khan's request for runtime testing of the unplug/unbind path. This patch supplies that testing and keeps Zheng's original fix shape. In vudc_probe(), v_init_timer() binds udc->tr_timer.timer to v_timer(). usbip_sockfd_store() starts the timer via v_start_timer()/v_kick_timer(). vudc_remove() can then free the containing struct vudc while the timer is still pending or executing. KASAN confirms the race on an unpatched x86_64 QEMU guest with CONFIG_KASAN=y, CONFIG_USBIP_VUDC=y, CONFIG_USB_ZERO=y, and a tight loop that repeatedly writes a socket fd to usbip_sockfd, closes the socket pair, and unbinds/rebinds usbip-vudc.0: BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x8ba/0x8e0 Write of size 8 at addr ffff888001b80740 by task trigger_and_unb/239 Allocated by task 239: vudc_probe+0x4d/0xaa0 Freed by task 239: kfree+0x18f/0x520 device_release_driver_internal+0x388/0x540 unbind_store+0xd9/0x100 This lands in the timer core rather than v_timer() itself because the embedded timer_list is being walked after its containing struct vudc has already been freed. The underlying lifetime bug is the same one Zheng reported. With v_stop_timer() called from vudc_remove() and the timer deleted synchronously, the same harness completed 5000 bind/unbind iterations with no KASAN report. Fixes: b6a0ca111867 ("usbip: vudc: Add UDC specific ops") Cc: stable Reported-by: Zheng Wang Closes: https://lore.kernel.org/linux-usb/20230317100954.2626573-1-zyytlz.wz@163.com/ Signed-off-by: Michael Bommarito Acked-by: Shuah Khan Link: https://patch.msgid.link/20260417163552.807548-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vudc_dev.c | 1 + drivers/usb/usbip/vudc_transfer.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index f11535020e35..a5c100107186 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); + v_stop_timer(udc); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index a4f02ea3e3ef..d4ce85c4c6a2 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc) { struct transfer_timer *t = &udc->tr_timer; - /* timer itself will take care of stopping */ + /* Delete the timer synchronously before teardown frees udc. */ dev_dbg(&udc->pdev->dev, "timer stop"); + timer_delete_sync(&t->timer); t->state = VUDC_TR_STOPPED; } From 75f6d3da2cc646983f41807ef98851569c12bca9 Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 5 May 2026 15:56:03 -0300 Subject: [PATCH 604/697] usb: usbtmc: check URB actual_length for interrupt-IN notifications commit 52f2ad3f7e5eb3b5908e1d685d4342519dc9cfcd upstream. USBTMC devices can use an optional interrupt endpoint for notification messages. These typically contain two-byte headers indicating the payload format, but the driver does not check if these headers are present before accessing the data buffers. In cases where the URB actual_length is not enough to fit these headers, the driver will either cause an out-of-bounds read, or consume stale leftover data from a previous notification. Fix by checking if actual_data contains enough bytes for the headers, otherwise resubmit URB to the interrupt endpoint. Fixes: dbf3e7f654c0 ("Implement an ioctl to support the USMTMC-USB488 READ_STATUS_BYTE operation.") Reported-by: syzbot+abbfd103085885cf16a2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=abbfd103085885cf16a2 Cc: stable Suggested-by: Michal Pecio Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260505-usbtmc-iin-size-v3-1-a36113f62db7@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 49459e3d34d6..4f28bf1d2b49 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2310,6 +2310,14 @@ static void usbtmc_interrupt(struct urb *urb) switch (status) { case 0: /* SUCCESS */ + /* ensure at least two bytes of headers were transferred */ + if (urb->actual_length < 2) { + dev_warn(dev, + "actual length %d not sufficient for interrupt headers\n", + urb->actual_length); + goto exit; + } + /* check for valid STB notification */ if (data->iin_buffer[0] > 0x81) { data->bNotify1 = data->iin_buffer[0]; From a90139ff1ebaff0e2b581827bd9290fbf34ed10f Mon Sep 17 00:00:00 2001 From: Heitor Alves de Siqueira Date: Tue, 5 May 2026 15:56:04 -0300 Subject: [PATCH 605/697] usb: usbtmc: reject interrupt endpoints with small wMaxPacketSize commit 121d2f682ba912b1427cddca7cf84840f41cc620 upstream. The USB488 subclass specification requires interrupt wMaxPacketSize to be 0x02, unless the device sends vendor-specific notifications. Endpoints that advertise less than 2 bytes for wMaxPacketSize are unlikely to work with the current driver, as URBs will not have enough space for interrupt headers. Considering that any notification URBs will be ignored by the driver, reject these endpoints early during probe. Fixes: 041370cce889 ("USB: usbtmc: refactor endpoint retrieval") Cc: stable Suggested-by: Michal Pecio Signed-off-by: Heitor Alves de Siqueira Link: https://patch.msgid.link/20260505-usbtmc-iin-size-v3-2-a36113f62db7@igalia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usbtmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 4f28bf1d2b49..fe88d555142a 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2444,6 +2444,12 @@ static int usbtmc_probe(struct usb_interface *intf, data->iin_ep = int_in->bEndpointAddress; data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in); data->iin_interval = int_in->bInterval; + /* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */ + if (iface_desc->desc.bInterfaceProtocol == 1 && + data->iin_wMaxPacketSize < 2) { + retcode = -EINVAL; + goto err_put; + } dev_dbg(&intf->dev, "Found Int in endpoint at %u\n", data->iin_ep); } From 02d9d8b79e18bc24ce89572868388c4478a6c2ae Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 12 May 2026 13:14:59 +0300 Subject: [PATCH 606/697] usb: typec: tipd: Fix error code in tps6598x_probe() commit b02900c85a6423cf9b3dcc6b47bf060c85075e69 upstream. Set the error code on these two error paths. The existing code returns success. Fixes: 77ed2f4538da ("usb: typec: tipd: Use read_power_status function in probe") Fixes: 04041fd7d6ec ("usb: typec: tipd: Read data status in probe and cache its value") Cc: stable Signed-off-by: Dan Carpenter Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/agL9o7wUK1dOVBTy@stanley.mountain Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tipd/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 01c657bc84e6..0c711dbe0c97 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1828,6 +1828,7 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; if (!tps6598x_read_power_status(tps)) goto err_unregister_port; if (!tps->data->read_data_status(tps)) From f34effb0b5456cc27bbbb15258785ad9fedf10c1 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Wed, 29 Apr 2026 18:33:32 +0200 Subject: [PATCH 607/697] usb: typec: tcpm: improve handling of DISCOVER_MODES failures commit c06e6cd488194e37ed4dc29d1488d1ffb760de60 upstream. UGREEN USB-C Multifunction Adapter Model CM512 (AKA "Revodok 107") exposes two SVIDs: 0xff01 (DP Alt Mode) and 0x1d5c. The DISCOVER_MODES step succeeds for 0xff01 and gets a NAK for 0x1d5c. Currently this results in DP Alt Mode not being registered either, since the modes are only registered once all of them have been discovered. The NAK results in the processing being stopped and thus no Alt modes being registered. Improve the situation by handling the NAK gracefully and continue processing the other modes. Before this change, the TCPM log ends like this: (more log entries before this) [ 5.028287] AMS DISCOVER_SVIDS finished [ 5.028291] cc:=4 [ 5.040040] SVID 1: 0xff01 [ 5.040054] SVID 2: 0x1d5c [ 5.040082] AMS DISCOVER_MODES start [ 5.040096] PD TX, header: 0x1b6f [ 5.050946] PD TX complete, status: 0 [ 5.059609] PD RX, header: 0x264f [1] [ 5.059626] Rx VDM cmd 0xff018043 type 1 cmd 3 len 2 [ 5.059640] AMS DISCOVER_MODES finished [ 5.059644] cc:=4 [ 5.069994] Alternate mode 0: SVID 0xff01, VDO 1: 0x000c0045 [ 5.070029] AMS DISCOVER_MODES start [ 5.070043] PD TX, header: 0x1d6f [ 5.081139] PD TX complete, status: 0 [ 5.087498] PD RX, header: 0x184f [1] [ 5.087515] Rx VDM cmd 0x1d5c8083 type 2 cmd 3 len 1 [ 5.087529] AMS DISCOVER_MODES finished [ 5.087534] cc:=4 (no further log entries after this point) After this patch the TCPM log looks exactly the same, but then continues like this: [ 5.100222] Skip SVID 0x1d5c (failed to discover mode) [ 5.101699] AMS DFP_TO_UFP_ENTER_MODE start (log goes on as the system initializes DP AltMode) Cc: stable Fixes: 41d9d75344d9 ("usb: typec: tcpm: add discover svids and discover modes support for sop'") Reviewed-by: Heikki Krogerus Signed-off-by: Sebastian Reichel Reviewed-by: RD Babiera Reviewed-by: Badhri Jagan Sridharan Link: https://patch.msgid.link/20260429-tcpm-discover-modes-nak-fix-v4-1-75945d0ed30f@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 97 ++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 36 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index e722c9fc3c97..e1ca6e7bbe55 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1989,6 +1989,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) tcpm_can_communicate_sop_prime(port); } +static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +{ + struct typec_port *typec = port->typec_port; + struct pd_mode_data *modep; + + if (rx_sop_type == TCPC_TX_SOP) { + modep = &port->mode_data; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + if (tcpm_cable_vdm_supported(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); + return 1; + } + + tcpm_register_partner_altmodes(port); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); + } + + return 0; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action, @@ -2246,41 +2295,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, } break; case CMD_DISCOVER_MODES: - if (rx_sop_type == TCPC_TX_SOP) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - u16 svid = modep->svids[modep->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP; - response[0] = VDO(svid, 1, svdm_version, - CMD_DISCOVER_MODES); - rlen = 1; - } else if (tcpm_cable_vdm_supported(port)) { - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(USB_SID_PD, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_SVID); - rlen = 1; - } else { - tcpm_register_partner_altmodes(port); - } - } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep_prime->svid_index++; - if (modep_prime->svid_index < modep_prime->nsvids) { - u16 svid = modep_prime->svids[modep_prime->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(svid, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_MODES); - rlen = 1; - } else { - tcpm_register_plug_altmodes(port); - tcpm_register_partner_altmodes(port); - } - } + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); break; case CMD_ENTER_MODE: *response_tx_sop_type = rx_sop_type; @@ -2323,9 +2342,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, switch (cmd) { case CMD_DISCOVER_IDENT: case CMD_DISCOVER_SVID: - case CMD_DISCOVER_MODES: case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): break; + case CMD_DISCOVER_MODES: + tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)", + PD_VDO_SVID_SVID0(p[0])); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); + break; case CMD_ENTER_MODE: /* Back to USB Operation */ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM; From d62d97c9c8c27801ee3930cc3723083a26c77036 Mon Sep 17 00:00:00 2001 From: Myrrh Periwinkle Date: Tue, 19 May 2026 18:41:39 +0700 Subject: [PATCH 608/697] usb: typec: ucsi: Check if power role change actually happened before handling commit b80e7d34c7ea6a564525119d6138fbb577a23dba upstream. The CrOS EC may send a connector status change event with the power direction changed flag set even if the power direction hasn't actually changed after initiating a SET_PDR command internally [1]. In practice this happens on every system suspend due to other changes performed by the EC [2][3][4], causing suspend to fail. Fix this by checking if the power role change actually happened before handling it. [1]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=1689;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [2]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=3923;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [3]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=5094;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 [4]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=2229;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 Cc: stable Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") Signed-off-by: Myrrh Periwinkle Reported-and-tested-by: Sergey Senozhatsky Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-1-6f1239535187@qtmlabs.xyz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 175c785347e7..52ef4cf8d508 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1224,7 +1224,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; u8 curr_scale, volt_scale; - enum typec_role role; + enum typec_role role, prev_role; u16 change; int ret; u32 val; @@ -1235,6 +1235,8 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); + prev_role = UCSI_CONSTAT(con, PWR_DIR); + ret = ucsi_get_connector_status(con, true); if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", @@ -1251,7 +1253,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) change = UCSI_CONSTAT(con, CHANGE); role = UCSI_CONSTAT(con, PWR_DIR); - if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); ucsi_port_psy_changed(con); From 62fbc1396108764cd069962906ce57598810a307 Mon Sep 17 00:00:00 2001 From: Myrrh Periwinkle Date: Tue, 19 May 2026 18:41:40 +0700 Subject: [PATCH 609/697] usb: typec: ucsi: Don't update power_supply on power role change if not connected commit d98d413ca65d0790a8f3695d0a5845538958ab84 upstream. We only need to update the power_supply on power role change if the port is connected, because otherwise the online status should be the same for both cases. Cc: stable Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") Signed-off-by: Myrrh Periwinkle Reported-and-tested-by: Sergey Senozhatsky Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-2-6f1239535187@qtmlabs.xyz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 52ef4cf8d508..34e93d6c6769 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1255,7 +1255,12 @@ static void ucsi_handle_connector_change(struct work_struct *work) if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); - ucsi_port_psy_changed(con); + + /* Some power_supply properties vary depending on the power direction when + * connected + */ + if (UCSI_CONSTAT(con, CONNECTED)) + ucsi_port_psy_changed(con); /* Complete pending power role swap */ if (!completion_done(&con->complete)) From 38ba1a464c0d141abf01e231f5c5bc949e851ed6 Mon Sep 17 00:00:00 2001 From: Jan Volckaert Date: Sun, 17 May 2026 17:32:37 +0200 Subject: [PATCH 610/697] USB: serial: option: add MeiG SRM813Q commit 7d2b37d3e42d19071b62f4ddbee6e16e905efbf1 upstream. Add support for the Qualcomm Technology Snapdragon X35-based MeiG SRM813Q module. The module can be put in different modes via AT commands to enable/disable GPS functionality: MODEM - PPP mode(2dee:4d63): AT+SER=1,1 If#= 0: RMNET If#= 1: DIAG/ADB If#= 2: MODEM If#= 3: AT P: Vendor=2dee ProdID=4d63 Rev=05.15 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=1bd51f0e C: #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms NMEA mode(2dee:4d64): AT+SER=51,1 If#= 0: RMNET If#= 1: DIAG/ADB If#= 2: NMEA If#= 3: AT P: Vendor=2dee ProdID=4d64 Rev=05.15 S: Manufacturer=MEIG S: Product=LTE-A Module S: SerialNumber=1bd51f0e C: #Ifs= 4 Cfg#= 1 Atr=80 MxPwr=500mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=50 Driver=qmi_wwan E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=60 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=85(I) Atr=03(Int.) MxPS= 10 Ivl=32ms I: If#= 3 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=03(Int.) MxPS= 10 Ivl=32ms Signed-off-by: Jan Volckaert Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 8add3a5477f6..c125a51e31ca 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2450,6 +2450,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */ + { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ From f7c3fcd634052548313ffaa7c83d350816a887fe Mon Sep 17 00:00:00 2001 From: Wanquan Zhong Date: Wed, 20 May 2026 19:32:45 +0800 Subject: [PATCH 611/697] USB: serial: option: add missing RSVD(5) flag for Rolling RW135R-GL commit 689f2facc689c8add11d7ff69fbbad17d65ee596 upstream. The RW135R-GL entry added in commit 01e8d0f74222 ("USB: serial: option: add support for Rolling Wireless RW135R-GL") was missing the .driver_info = RSVD(5) flag used by other Rolling Wireless MBIM laptop modules (e.g. RW135-GL and RW350-GL). Without this flag, the option driver incorrectly binds to the reserved ADB interface (If#5) in multi-interface USB modes, causing AT/MBIM communication failures after mode switching. This matches the handling of other Rolling Wireless MBIM devices. - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, diag, AT, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 8 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, diag, AT, ADB, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 7 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 6 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=30 Driver=option E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=40 Driver=option E: Ad=86(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=42 Prot=01 Driver=(none) E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms - VID:PID 33f8:1003, RW135R-GL for laptop debug M.2 cards (with MBIM interface for Linux/Chrome OS) 0x1003: mbim, pipe Here are the outputs of usb-devices: T: Bus=03 Lev=01 Prnt=01 Port=04 Cnt=02 Dev#= 9 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=33f8 ProdID=1003 Rev= 5.15 S: Manufacturer=Rolling Wireless S.a.r.l. S: Product=Rolling RW135R-GL Module S: SerialNumber=12345678 C:* #Ifs= 3 Cfg#= 1 Atr=a0 MxPwr=500mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver=cdc_mbim E: Ad=82(I) Atr=03(Int.) MxPS= 64 Ivl=32ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver=cdc_mbim E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=option E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Fixes: 01e8d0f74222 ("USB: serial: option: add support for Rolling Wireless RW135R-GL") Signed-off-by: Wanquan Zhong Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c125a51e31ca..c8f0d2bbfc1b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2476,7 +2476,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, - { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, From 22823a319fb2afdf02cacafbed8b613b757efbc8 Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Tue, 19 May 2026 19:11:50 +0800 Subject: [PATCH 612/697] USB: serial: belkin_sa: validate interrupt status length commit 4ce058df2ee02cc2a0f0fd5cd64ce6f1482a0b65 upstream. The Belkin interrupt callback treats interrupt data as a four-byte status report and reads LSR/MSR fields at offsets 2 and 3. The interrupt-in buffer length is derived from endpoint wMaxPacketSize, and short interrupt transfers may complete successfully with a smaller actual_length. Check the completed interrupt packet length before parsing status fields so short interrupt endpoints and short successful packets are ignored instead of causing out-of-bounds or stale status-byte reads. KASAN report as below: BUG: KASAN: slab-out-of-bounds in belkin_sa_read_int_callback() Read of size 1 Call trace: belkin_sa_read_int_callback() (drivers/usb/serial/belkin_sa.c:202) __usb_hcd_giveback_urb() (drivers/usb/core/hcd.c:1630) dummy_timer() (?:?) Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/belkin_sa.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index aa6b4c4ad5ec..62c853ab18a6 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb) usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); + if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1) + goto exit; + /* Handle known interrupt data */ /* ignore data[0] and data[1] */ From ae03453f2c809ca3cf73753269fa6184dea7160f Mon Sep 17 00:00:00 2001 From: Zhang Cen Date: Fri, 22 May 2026 22:54:42 +0800 Subject: [PATCH 613/697] USB: serial: cypress_m8: validate interrupt packet headers commit 9f9bfc80c67f35a275820da7e83a35dface08281 upstream. cypress_read_int_callback() parses the interrupt-in buffer according to the selected Cypress packet format. Format 1 has a two-byte status/count header and format 2 has a one-byte combined status/count header. The usb-serial core sizes the interrupt-in buffer from the endpoint descriptor's wMaxPacketSize, and successful interrupt transfers can complete short when URB_SHORT_NOT_OK is not set. Check that the completed packet contains the selected header before reading it. Malformed short reports are ignored and the interrupt URB is resubmitted through the existing retry path, preventing out-of-bounds header-byte reads. KASAN report as below: KASAN slab-out-of-bounds in cypress_read_int_callback+0x240/0x7f0 Read of size 1 Call trace: cypress_read_int_callback() (drivers/usb/serial/cypress_m8.c:1009) __usb_hcd_giveback_urb() dummy_timer() Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Assisted-by: Codex:gpt-5.5 Signed-off-by: Zhang Cen Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Cc: stable@vger.kernel.org # 2.6.26 [ johan: use constants in header length sanity checks ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index e29569d65991..eb47f35aab0c 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1017,8 +1017,8 @@ static void cypress_read_int_callback(struct urb *urb) char tty_flag = TTY_NORMAL; int bytes = 0; int result; - int i = 0; int status = urb->status; + int i; switch (status) { case 0: /* success */ @@ -1056,22 +1056,32 @@ static void cypress_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); result = urb->actual_length; + i = 0; switch (priv->pkt_fmt) { default: case packet_format_1: /* This is for the CY7C64013... */ + if (result < 2) + break; priv->current_status = data[0] & 0xF8; bytes = data[1] + 2; i = 2; break; case packet_format_2: /* This is for the CY7C63743... */ + if (result < 1) + break; priv->current_status = data[0] & 0xF8; bytes = (data[0] & 0x07) + 1; i = 1; break; } spin_unlock_irqrestore(&priv->lock, flags); + if (i == 0) { + dev_dbg(dev, "%s - short packet received: %d bytes\n", + __func__, result); + goto continue_read; + } if (result < bytes) { dev_dbg(dev, "%s - wrong packet size - received %d bytes but packet said %d bytes\n", From ea2b792330b44b6d7ce671c3e1d59d0c121f7ed1 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:26:48 +0200 Subject: [PATCH 614/697] USB: serial: keyspan: fix missing indat transfer sanity check commit ab8336a7e414f018430aa1af3a46944032f7ff96 upstream. Add the missing sanity check on the size of usa49wg indat transfers to avoid parsing stale or uninitialised slab data. Fixes: 0ca1268e109a ("USB Serial Keyspan: add support for USA-49WG & USA-28XG") Cc: stable@vger.kernel.org # 2.6.23 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 9129e0282c24..baae11b2fa7b 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb) len = 0; while (i < urb->actual_length) { + if (urb->actual_length - i < 3) { + dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n"); + break; + } /* Check port number from message */ if (data[i] >= serial->num_ports) { From 6c0cf56f00f280d72180bb6ce79741bc787a6269 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 22 May 2026 16:19:50 +0200 Subject: [PATCH 615/697] USB: serial: mxuport: fix memory corruption with small endpoint commit 4085f0dbb1ce2251c9a5938d693de6593f0ab2bd upstream. Make sure that the bulk-out endpoint max packet size is at least eight bytes to avoid user-controlled slab corruption should a malicious device report a smaller size. Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") Cc: stable@vger.kernel.org # 3.14 Cc: Andrew Lunn Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mxuport.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ad5fdf55a02e..c9b9928c473a 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, */ BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); + /* + * The bulk-out buffers must be large enough for the four-byte header + * (and following data), but assume anything smaller than eight bytes + * is broken. + */ + if (usb_endpoint_maxp(epds->bulk_out[0]) < 8) + return -EINVAL; + for (i = 1; i < num_ports; ++i) epds->bulk_out[i] = epds->bulk_out[0]; From f06bcaba29707f060706483b2020d3cafbe98f9f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 20 May 2026 16:27:10 +0200 Subject: [PATCH 616/697] USB: serial: mct_u232: fix missing interrupt-in transfer sanity check commit 245aba83e3c288e176ed037a1f6b618b09e92ed8 upstream. Add the missing sanity check on the size of interrupt-in transfers to avoid parsing stale or uninitialised slab data (and leaking it to user space). Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mct_u232.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 2bce8cc03aca..d225d7c1455f 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -543,6 +543,11 @@ static void mct_u232_read_int_callback(struct urb *urb) goto exit; } + if (urb->actual_length < 2) { + dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n"); + goto exit; + } + /* * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. From caec0145e5974e85fe5192fc6a6f5aa1a98f82a6 Mon Sep 17 00:00:00 2001 From: Kai Aizen Date: Thu, 30 Apr 2026 20:56:43 +0300 Subject: [PATCH 617/697] usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind commit 68aa70648b625fa684bc0b71bbfd905f4943ca20 upstream. uvc_function_bind() walks &opts->extension_units twice without holding opts->lock: - directly, for the iExtension string-descriptor fixup loop; - indirectly, four times via uvc_copy_descriptors() (once per speed), where the helper iterates uvc->desc.extension_units (which aliases &opts->extension_units) to size and emit XU descriptors. The configfs side (uvcg_extension_make / uvcg_extension_drop, in drivers/usb/gadget/function/uvc_configfs.c) takes opts->lock around its list_add_tail / list_del operations. A privileged userspace process that holds the configfs subtree open and writes the gadget UDC name to bind the function while concurrently rmdir()'ing an extensions subdir can race uvcg_extension_drop() against the bind-time list walks and dereference a freed struct uvcg_extension. Hold opts->lock from the start of the XU string-descriptor fixup through the last uvc_copy_descriptors() call, releasing on the descriptor-error path via a new error_unlock label that drops the lock before falling through to the existing error label. This matches the locking discipline of the configfs callbacks and removes the only remaining unsynchronised reader of the XU list during bind. Reachability: only privileged processes that can mount configfs and write to gadget UDC files can trigger the race, so this is a correctness fix rather than a security boundary. Fixes: 0525210c9840 ("usb: gadget: uvc: Allow definition of XUs in configfs") Cc: stable Signed-off-by: Kai Aizen Link: https://patch.msgid.link/20260430175643.67120-1-kai.aizen.dev@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_uvc.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 842187a09cc0..72601e15563c 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -768,6 +768,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; + /* + * Hold opts->lock across both the XU string-descriptor fixup below and + * the descriptor-copy block further down. Without this, configfs + * uvcg_extension_drop() (which takes opts->lock) can race with the + * list_for_each_entry() walks here and inside uvc_copy_descriptors(), + * leading to a UAF on a freed struct uvcg_extension. See + * drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop(). + */ + mutex_lock(&opts->lock); + /* * XUs can have an arbitrary string descriptor describing them. If they * have one pick up the ID. @@ -785,7 +795,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { ret = PTR_ERR(us); - goto error; + goto error_unlock; } uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id : @@ -799,14 +809,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_iad.bFirstInterface = ret; uvc_control_intf.bInterfaceNumber = ret; uvc->control_intf = ret; opts->control_interface = ret; if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_streaming_intf_alt0.bInterfaceNumber = ret; uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; @@ -817,30 +827,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (IS_ERR(f->fs_descriptors)) { ret = PTR_ERR(f->fs_descriptors); f->fs_descriptors = NULL; - goto error; + goto error_unlock; } f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); if (IS_ERR(f->hs_descriptors)) { ret = PTR_ERR(f->hs_descriptors); f->hs_descriptors = NULL; - goto error; + goto error_unlock; } f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); if (IS_ERR(f->ss_descriptors)) { ret = PTR_ERR(f->ss_descriptors); f->ss_descriptors = NULL; - goto error; + goto error_unlock; } f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS); if (IS_ERR(f->ssp_descriptors)) { ret = PTR_ERR(f->ssp_descriptors); f->ssp_descriptors = NULL; - goto error; + goto error_unlock; } + mutex_unlock(&opts->lock); + /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); @@ -872,6 +884,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; +error_unlock: + mutex_unlock(&opts->lock); v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); error: From e6f8be12f0307145b9a6010f044925952b37de8b Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 27 Apr 2026 23:36:51 +0800 Subject: [PATCH 618/697] usb: gadget: net2280: Fix double free in probe error path commit c8547c74988e0b5f4cbb1b895e2a57aae084f070 upstream. usb_initialize_gadget() installs gadget_release() as the release callback for the embedded gadget device. The struct net2280 instance is therefore released through gadget_release() when the gadget device's last reference is dropped. The probe error path calls net2280_remove(), which tears down the partially initialized device and drops the gadget reference with usb_put_gadget(). Calling kfree(dev) afterwards can free the same object again. Drop the explicit kfree() and let the gadget device release callback handle the final free. This issue was found by a static analysis tool I am developing. Fixes: f770fbec4165 ("USB: UDC: net2280: Fix memory leaks") Cc: stable Signed-off-by: Guangshuo Li Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260427153651.337846-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/net2280.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 8ea1adc7461d..07899a2c35a7 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; done: - if (dev) { + if (dev) net2280_remove(pdev); - kfree(dev); - } return retval; } From f928630f450be7008e1dce86bf95275b2f92af91 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Apr 2026 22:21:19 +0800 Subject: [PATCH 619/697] usb: gadget: f_hid: fix device reference leak in hidg_alloc() commit 4f88d65def6f3c90121601b4f62a4c967f3063a6 upstream. hidg_alloc() initializes hidg->dev with device_initialize() before calling dev_set_name(). If dev_set_name() fails, the function currently jumps to err_unlock and returns without calling put_device(). This leaves the device reference unbalanced and prevents hidg_release() from being called. Calling put_device() here is also safe, since hidg_release() only frees resources owned by hidg. The issue was identified by a static analysis tool I developed and confirmed by manual review. Route the dev_set_name() failure path through err_put_device so the device reference is dropped properly. Fixes: 89ff3dfac604 ("usb: gadget: f_hid: fix f_hidg lifetime vs cdev") Cc: stable Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold johan@kernel.org Link: https://patch.msgid.link/20260413142119.2977716-1-lgs201920130244@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_hid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 1004c577b50e..ee59e6516187 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1621,7 +1621,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) hidg->dev.devt = MKDEV(major, opts->minor); ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor); if (ret) - goto err_unlock; + goto err_put_device; hidg->bInterfaceSubClass = opts->subclass; hidg->bInterfaceProtocol = opts->protocol; @@ -1658,7 +1658,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) err_put_device: put_device(&hidg->dev); -err_unlock: mutex_unlock(&opts->lock); return ERR_PTR(ret); } From f8f5a8f48c7cae3fac85e04b593bd47939f9725f Mon Sep 17 00:00:00 2001 From: Jeremy Erazo Date: Tue, 12 May 2026 16:05:30 +0000 Subject: [PATCH 620/697] usb: gadget: composite: fix integer underflow in WebUSB GET_URL handling commit 6c5dbc104dadd79fc2923497c20bae759a18758c upstream. The WebUSB GET_URL handler in composite_setup() narrows landing_page_length to fit the host-supplied wLength using landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; If wLength is smaller than WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH the unsigned subtraction wraps, and the subsequent memcpy(url_descriptor->URL, cdev->landing_page + landing_page_offset, landing_page_length - landing_page_offset); ends up copying close to UINT_MAX bytes from cdev->landing_page into cdev->req->buf. KASAN reports a slab-out-of-bounds in composite_setup on the kmalloc-2k gadget_info allocation, and FORTIFY_SOURCE traps the memcpy as a 4294967293-byte field-spanning write into url_descriptor->URL (size 252). A USB host can reach this from a single SETUP packet against any gadget that has webusb/use=1 and a landingPage configured. Handle the small-wLength case before the math: when the host requested fewer bytes than the URL descriptor header, only the header is meaningful and no URL bytes need to be copied. Setting landing_page_length to landing_page_offset makes the existing memcpy a no-op and leaves the descriptor returned to the host unchanged for all larger wLength values. Fixes: 93c473948c58 ("usb: gadget: add WebUSB landing page support") Cc: stable Signed-off-by: Jeremy Erazo Link: https://patch.msgid.link/20260512160530.352318-1-mendozayt13@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 5b3866909b75..bb3fa6fa9e8b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2172,7 +2172,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) sizeof(url_descriptor->URL) - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); - if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) + if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH) + landing_page_length = landing_page_offset; + else if (w_length < + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; From 5933063935e8404c9a0504c4ed96260a0530196d Mon Sep 17 00:00:00 2001 From: Seungjin Bae Date: Mon, 18 May 2026 19:43:14 -0400 Subject: [PATCH 621/697] usb: gadget: dummy_hcd: Reject hub port requests for non-existent ports commit 7d9633528dd40e33964d2dc74a5abbf5c4d116ce upstream. The `dummy_hub_control()` function handles USB hub class requests to the virtual root hub. The `GetPortStatus` case returns -EPIPE for requests with `wIndex != 1`, since the virtual root hub has only a single port. However, the `ClearPortFeature` and `SetPortFeature` cases lack the same check. Fix this by extending the `wIndex != 1` rejection to both cases, matching the existing behavior of `GetPortStatus`. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Suggested-by: Alan Stern Signed-off-by: Seungjin Bae Reviewed-by: Alan Stern Link: https://patch.msgid.link/20260518234314.1889396-1-eeodqql09@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index da271308d753..4a78536644ba 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2134,6 +2134,8 @@ static int dummy_hub_control( case ClearHubFeature: break; case ClearPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed == HCD_USB3) { @@ -2248,6 +2250,8 @@ static int dummy_hub_control( retval = -EPIPE; break; case SetPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_LINK_STATE: if (hcd->speed != HCD_USB3) { From 607730a414773a7cbe3037a64a6c64e72689ff5e Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 12:03:59 -0400 Subject: [PATCH 622/697] usb: gadget: f_fs: copy only received bytes on short ep0 read commit 4e036c10e7f4df5d951c69cc3697bc8e209c6d02 upstream. ffs_ep0_read() allocates its control-OUT data buffer with kmalloc() (not kzalloc) at the Length value from the Setup packet, then copies that full len to userspace regardless of how many bytes were actually received: data = kmalloc(len, GFP_KERNEL); ... ret = __ffs_ep0_queue_wait(ffs, data, len); if ((ret > 0) && (copy_to_user(buf, data, len))) ret = -EFAULT; __ffs_ep0_queue_wait() returns req->actual, which on a short control OUT transfer is strictly less than len. The copy_to_user() call still copies len bytes, so on a short OUT the last (len - ret) bytes of the kmalloc() buffer -- uninitialised slab residue -- are delivered to the FunctionFS daemon. Short ep0 OUT completions are specified USB control-transfer behavior and are produced by in-tree UDCs: * dwc2 continues on req->actual < req->length for ep0 DATA OUT (short-not-ok is the only ep0-OUT stall path). * aspeed_udc ends ep0 OUT on rx_len < ep->ep.maxpacket. * renesas_usbf logs "ep0 short packet" and completes the request. * dwc3 stalls on short IN but not on short OUT. A short ep0 OUT is therefore not evidence of a broken UDC; it is a normal condition f_fs has to cope with. The sibling gadgetfs implementation in drivers/usb/gadget/legacy/inode.c already does this correctly via min(len, dev->req->actual) before copy_to_user(). This patch brings f_fs.c to the same safe pattern rather than trimming at a defensive layer. The bug is reached from the FunctionFS device node, which in real deployments is owned by the privileged gadget daemon (adbd, UMS, composite gadget services, etc.); it is not reachable from unprivileged userspace. Linux host stacks normally reject short-wLength control OUTs before they reach the gadget, so reproducing this required a build that bypasses that host-side check. With the bypass in place, a 1-byte payload on a 64-byte Setup produces 63 bytes of non-canary slab residue in the daemon's read buffer. Fix by copying only ret (actually received) bytes to userspace. Fixes: ddf8abd25994 ("USB: f_fs: the FunctionFS driver") Cc: stable Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260419160359.1577270-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 497d20260b04..f8bd421b344c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -622,7 +622,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ ret = __ffs_ep0_queue_wait(ffs, data, len); - if ((ret > 0) && (copy_to_user(buf, data, len))) + if ((ret > 0) && (copy_to_user(buf, data, ret))) ret = -EFAULT; goto done_mutex; From c7d421123b98d5e9c1c84bd9957aba36f1cbb4ca Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 19 Apr 2026 12:12:27 -0400 Subject: [PATCH 623/697] usb: gadget: f_fs: serialize DMABUF cancel against request completion commit 2796646f6d892c1eb6818c7ca41fdfa12568e8d1 upstream. ffs_epfile_dmabuf_io_complete() calls usb_ep_free_request() on the completed request but leaves priv->req, the back-pointer that ffs_dmabuf_transfer() set on submission, pointing at the freed memory. A later FUNCTIONFS_DMABUF_DETACH ioctl or ffs_epfile_release() on the close path still sees priv->req non-NULL under ffs->eps_lock: if (priv->ep && priv->req) usb_ep_dequeue(priv->ep, priv->req); so usb_ep_dequeue() is called on a freed usb_request. On dummy_hcd the dequeue path only walks a live queue and pointer-compares, so the freed pointer reads without faulting and KASAN requires an explicit check at the FunctionFS call site to surface the use-after-free. On SG-capable in-tree UDCs the dequeue path dereferences the supplied request immediately: * chipidea's ep_dequeue() does container_of(req, struct ci_hw_req, req) and reads hwreq->req.status before acquiring its own lock. * cdnsp's cdnsp_gadget_ep_dequeue() reads request->status first. The narrower option of clearing priv->req via cmpxchg() in the completion does not close the race: the completion runs without eps_lock, so a cancel path holding eps_lock can still observe priv->req non-NULL, race a concurrent completion that clears and frees, and pass the freed pointer to usb_ep_dequeue(). A slightly longer fix that moves the free into the cleanup work is needed. Same class of lifetime race as the recent usbip-vudc timer fix [1]. Take eps_lock in the sole place that mutates priv->req from the callback direction by moving usb_ep_free_request() out of the completion into ffs_dmabuf_cleanup(), the existing work handler scheduled by ffs_dmabuf_signal_done() on ffs->io_completion_wq. Clear priv->req there under eps_lock before freeing, and only clear if priv->req still names our request (a subsequent ffs_dmabuf_transfer() on the same attachment may have queued a new one). This keeps the existing dummy_hcd sync-dequeue invariant: the completion callback is still invoked by the UDC without eps_lock held (dummy_hcd drops its own lock before calling the callback), and the callback now takes no f_fs lock at all. Serialization against the cancel path happens in cleanup, which runs from the workqueue with no f_fs lock held on entry. The priv ref count protects the containing ffs_dmabuf_priv: ffs_dmabuf_transfer() takes a ref via ffs_dmabuf_get(), cleanup drops it via ffs_dmabuf_put(), so priv stays live for the cleanup even after the cancel path's list_del + ffs_dmabuf_put. The ffs_dmabuf_transfer() error path no longer frees usb_req inline: fence->req and fence->ep are set before usb_ep_queue(), so ffs_dmabuf_cleanup() (scheduled by the error-path ffs_dmabuf_signal_done()) owns the free regardless of whether the queue succeeded. Reproduced under KASAN on both detach and close paths against dummy_hcd with an observability hook (kasan_check_byte(priv->req) immediately before usb_ep_dequeue) at the two FunctionFS cancel sites to surface the stale-pointer access; the hook is not part of this patch. The KASAN allocator / free stacks in the captured splats identify the same request: alloc in dummy_alloc_request, free in dummy_timer, fault reached from ffs_epfile_release (close) and from the FUNCTIONFS_DMABUF_DETACH ioctl (detach). With the patch applied, both paths are silent under the same hook. The bug is reached from the FunctionFS device node, which in real deployments is owned by the privileged gadget daemon (adbd, UMS, composite gadget services, etc.); it is not reachable from unprivileged userspace or from a USB host on the cable. FunctionFS mounts default to GLOBAL_ROOT_UID, but the filesystem supports uid=, gid=, and fmode= delegation to a non-root gadget daemon, so on real deployments the attacker may be a less-privileged service rather than root. Fixes: 7b07a2a7ca02 ("usb: gadget: functionfs: Add DMABUF import interface") Link: https://lore.kernel.org/all/20260417163552.807548-1-michael.bommarito@gmail.com/ [1] Cc: stable Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Link: https://patch.msgid.link/20260419161227.1587668-1-michael.bommarito@gmail.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index f8bd421b344c..737d60b26e02 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -151,6 +151,8 @@ struct ffs_dma_fence { struct dma_fence base; struct ffs_dmabuf_priv *priv; struct work_struct work; + struct usb_ep *ep; + struct usb_request *req; }; struct ffs_epfile { @@ -1365,6 +1367,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work) struct ffs_dmabuf_priv *priv = dma_fence->priv; struct dma_buf_attachment *attach = priv->attach; struct dma_fence *fence = &dma_fence->base; + struct usb_request *req = dma_fence->req; + struct usb_ep *ep = dma_fence->ep; + + /* + * eps_lock pairs with the cancel paths so they cannot pass a freed + * req to usb_ep_dequeue(). Only clear if priv->req still names ours; + * a re-queue on the same attachment may have taken that slot. + */ + spin_lock_irq(&priv->ffs->eps_lock); + if (priv->req == req) + priv->req = NULL; + spin_unlock_irq(&priv->ffs->eps_lock); + + if (ep && req) + usb_ep_free_request(ep, req); ffs_dmabuf_put(attach); dma_fence_put(fence); @@ -1394,8 +1411,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, struct usb_request *req) { pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + /* req is freed by ffs_dmabuf_cleanup() under eps_lock. */ ffs_dmabuf_signal_done(req->context, req->status); - usb_ep_free_request(ep, req); } static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) @@ -1679,6 +1696,10 @@ static int ffs_dmabuf_transfer(struct file *file, usb_req->context = fence; usb_req->complete = ffs_epfile_dmabuf_io_complete; + /* ffs_dmabuf_cleanup() frees usb_req via these two fields. */ + fence->req = usb_req; + fence->ep = ep->ep; + cookie = dma_fence_begin_signalling(); ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); dma_fence_end_signalling(cookie); @@ -1688,7 +1709,6 @@ static int ffs_dmabuf_transfer(struct file *file, } else { pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); ffs_dmabuf_signal_done(fence, ret); - usb_ep_free_request(ep->ep, usb_req); } spin_unlock_irq(&epfile->ffs->eps_lock); From 31b98e503ecca8077e5247253dd5425ab84bc96d Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:56 -0400 Subject: [PATCH 624/697] thunderbolt: property: Reject u32 wrap in tb_property_entry_valid() commit 01deda0152066c6c955f0619114ea6afa070aaec upstream. entry->value is u32 and entry->length is u16; the sum is performed in u32 and wraps. A malicious XDomain peer can pick value = 0xffffff00, length = 0x100 so the sum 0x100000000 wraps to 0 and passes the > block_len check. tb_property_parse() then passes entry->value to parse_dwdata() as a dword offset into the property block, reading attacker-directed memory far past the allocation. For TEXT-typed entries with the "deviceid" or "vendorid" keys this lands in xd->device_name / xd->vendor_name and is readable back via the per-XDomain device_name / vendor_name sysfs attributes; the leak is NUL-bounded (kstrdup() stops at the first zero byte) and untargeted (the attacker picks a delta, not an absolute address). DATA-typed entries are parsed into property->value.data but not generically surfaced to userspace. Use check_add_overflow() so a wrapped sum is rejected. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/property.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 31aa0516932a..6133d46fd79c 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -52,13 +53,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords) static bool tb_property_entry_valid(const struct tb_property_entry *entry, size_t block_len) { + u32 end; + switch (entry->type) { case TB_PROPERTY_TYPE_DIRECTORY: case TB_PROPERTY_TYPE_DATA: case TB_PROPERTY_TYPE_TEXT: if (entry->length > block_len) return false; - if (entry->value + entry->length > block_len) + if (check_add_overflow(entry->value, entry->length, &end) || + end > block_len) return false; break; From d548179adcc87e1bc66b17e00352a1f536e76065 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 10 May 2026 19:16:57 -0400 Subject: [PATCH 625/697] thunderbolt: property: Reject dir_len < 4 to prevent size_t underflow commit de21b59c29e31c5108ddc04210631bbfab81b997 upstream. On the non-root path, __tb_property_parse_dir() takes dir_len from entry->length (u16 widened to size_t). Two distinct OOB conditions follow when entry->length < 4: 1. The non-root path begins with kmemdup(&block[dir_offset], sizeof(*dir->uuid), ...) which always reads 4 dwords from dir_offset. tb_property_entry_valid() only enforces dir_offset + entry->length <= block_len, so a crafted entry with dir_offset close to the end of the property block and entry->length in 0..3 passes that gate but lets the UUID copy run off the block (e.g. dir_offset = 497, dir_len = 3 in a 500-dword block reads block[497..501]). 2. After the kmemdup, content_len = dir_len - 4 underflows size_t to ~SIZE_MAX, nentries becomes SIZE_MAX / 4, and the entry walk runs OOB on each iteration until an entry fails validation or the kernel oopses on an unmapped page. Reject dir_len < 4 on the non-root path *before* the UUID kmemdup, which closes both holes. Also move INIT_LIST_HEAD(&dir->properties) up to immediately after the dir allocation so the new error-return path (and the existing uuid-alloc failure path) calling tb_property_free_dir() sees a walkable list rather than the zero-initialized NULL next/prev that list_for_each_entry_safe() would oops on. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/property.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 6133d46fd79c..e3fdd086135b 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -174,10 +174,16 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, if (!dir) return NULL; + INIT_LIST_HEAD(&dir->properties); + if (is_root) { content_offset = dir_offset + 2; content_len = dir_len; } else { + if (dir_len < 4) { + tb_property_free_dir(dir); + return NULL; + } dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), GFP_KERNEL); if (!dir->uuid) { @@ -191,8 +197,6 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, entries = (const struct tb_property_entry *)&block[content_offset]; nentries = content_len / (sizeof(*entries) / 4); - INIT_LIST_HEAD(&dir->properties); - for (i = 0; i < nentries; i++) { struct tb_property *property; From 14dd80a20a72ce334adcc2d67402360527065948 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 18 May 2026 10:43:07 -0400 Subject: [PATCH 626/697] scsi: fcoe: Reject FIP descriptors with zero fip_dlen in CVL walker commit 9eed1bd59937e6828b00d2f2dfef631d964f3636 upstream. drivers/scsi/fcoe/fcoe_ctlr.c::fcoe_ctlr_recv_clr_vlink() advanced the descriptor cursor by an attacker-supplied fip_dlen without ever requiring dlen >= sizeof(struct fip_desc) in the default branch. The named descriptor cases (FIP_DT_MAC, FIP_DT_NAME, FIP_DT_VN_ID) checked their per-type minimum lengths, but a FIP_DT_NON_CRITICAL descriptor (fip_dtype >= 128, which the standard requires receivers to silently ignore) skipped that check entirely. An unauthenticated L2 peer on the FCoE control VLAN could hang fcoe_ctlr_recv_work on an fcoe, qedf, or bnx2fc initiator indefinitely by emitting one FIP CVL frame whose single descriptor had fip_dtype == FIP_DT_NON_CRITICAL and fip_dlen == 0: the cursor advanced zero bytes per iteration and the loop condition rlen >= sizeof(*desc) stayed true forever, blocking every subsequent FIP frame on that controller. Tighten the outer dlen guard to also reject dlen < sizeof(struct fip_desc), so a malformed descriptor whose length cannot even cover the descriptor header is rejected before the switch. This is the same lower-bound the named cases already apply and is the minimum scope that closes the loop. Fixes: 97c8389d54b9 ("[SCSI] fcoe, libfcoe: Add support for FIP. FCoE discovery and keep-alive.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Hannes Reinecke Link: https://patch.msgid.link/20260518144307.2820961-1-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/fcoe/fcoe_ctlr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/fcoe/fcoe_ctlr.c b/drivers/scsi/fcoe/fcoe_ctlr.c index 8e4241c295e3..59b7dd86e4b2 100644 --- a/drivers/scsi/fcoe/fcoe_ctlr.c +++ b/drivers/scsi/fcoe/fcoe_ctlr.c @@ -1386,7 +1386,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; - if (dlen > rlen) + if (dlen < sizeof(*desc) || dlen > rlen) goto err; /* Drop CVL if there are duplicate critical descriptors */ if ((desc->fip_dtype < 32) && From 35461d23744175a78b6280293892cca357c22793 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 20 May 2026 09:30:15 -0400 Subject: [PATCH 627/697] scsi: scsi_transport_fc: Widen FPIN pname walker counter to u32 commit a9a39233ec1fc9f97ea1340a4d09bb7ec2be5153 upstream. An adjacent Fibre Channel fabric actor that can deliver an FPIN ELS frame to an lpfc or qla2xxx Linux initiator can trigger a non-return in the generic FC transport. This is not a local userspace or IP network path; the attacker must be able to inject fabric traffic, for example as a compromised switch or fabric controller, or as a same-zone N_Port on a fabric that permits source spoofing. The Link-Integrity and Peer-Congestion FPIN walkers used a u8 loop counter against the 32-bit on-wire pname_count field, and did not bound pname_count by the descriptor body already validated by the TLV walker. A pname_count of 256 therefore wraps the counter and keeps the loop condition true indefinitely. Factor the shared pname_list[] walk into one helper, widen the counter to u32, and clamp pname_count against the entries that fit in the descriptor body before iterating. Fixes: 3dcfe0de5a97 ("scsi: fc: Parse FPIN packets and update statistics") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Christoph Hellwig Reviewed-by: John Garry Link: https://patch.msgid.link/20260520133015.1018937-1-michael.bommarito@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_transport_fc.c | 77 +++++++++++++++++--------------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 3a821afee9bc..2661cf9908a5 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -735,6 +735,37 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) } } +static void +fc_fpin_pname_stats_update(struct Scsi_Host *shost, + struct fc_rport *attach_rport, u16 event_type, + u32 desc_len, u32 fixed_len, u32 pname_count, + __be64 *pname_list, + void (*stats_update)(u16 event_type, + struct fc_fpin_stats *stats)) +{ + u32 i; + struct fc_rport *rport; + u64 wwpn; + + if (desc_len < fixed_len) + pname_count = 0; + else + pname_count = min(pname_count, (desc_len - fixed_len) / + sizeof(pname_list[0])); + + for (i = 0; i < pname_count; i++) { + wwpn = be64_to_cpu(pname_list[i]); + rport = fc_find_rport_by_wwpn(shost, wwpn); + if (rport && + (rport->roles & FC_PORT_ROLE_FCP_TARGET || + rport->roles & FC_PORT_ROLE_NVME_TARGET)) { + if (rport == attach_rport) + continue; + stats_update(event_type, &rport->fpin_stats); + } + } +} + /* * fc_fpin_li_stats_update - routine to update Link Integrity * event statistics. @@ -745,13 +776,11 @@ fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats) static void fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_host_attrs *fc_host = shost_to_fc_host(shost); struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; u16 event_type = be16_to_cpu(li_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(li_desc->attached_wwpn)); @@ -762,22 +791,11 @@ fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) fc_li_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(li_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(li_desc->pname_count); - i++) { - wwpn = be64_to_cpu(li_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_li_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(li_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*li_desc), + be32_to_cpu(li_desc->pname_count), + li_desc->pname_list, fc_li_stats_update); if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn)) fc_li_stats_update(event_type, &fc_host->fpin_stats); @@ -825,13 +843,11 @@ static void fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv) { - u8 i; struct fc_rport *rport = NULL; struct fc_rport *attach_rport = NULL; struct fc_fn_peer_congn_desc *pc_desc = (struct fc_fn_peer_congn_desc *)tlv; u16 event_type = be16_to_cpu(pc_desc->event_type); - u64 wwpn; rport = fc_find_rport_by_wwpn(shost, be64_to_cpu(pc_desc->attached_wwpn)); @@ -842,22 +858,11 @@ fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost, fc_cn_stats_update(event_type, &attach_rport->fpin_stats); } - if (be32_to_cpu(pc_desc->pname_count) > 0) { - for (i = 0; - i < be32_to_cpu(pc_desc->pname_count); - i++) { - wwpn = be64_to_cpu(pc_desc->pname_list[i]); - rport = fc_find_rport_by_wwpn(shost, wwpn); - if (rport && - (rport->roles & FC_PORT_ROLE_FCP_TARGET || - rport->roles & FC_PORT_ROLE_NVME_TARGET)) { - if (rport == attach_rport) - continue; - fc_cn_stats_update(event_type, - &rport->fpin_stats); - } - } - } + fc_fpin_pname_stats_update(shost, attach_rport, event_type, + be32_to_cpu(pc_desc->desc_len), + FC_TLV_DESC_LENGTH_FROM_SZ(*pc_desc), + be32_to_cpu(pc_desc->pname_count), + pc_desc->pname_list, fc_cn_stats_update); } /* From 89c81d1228c00fa6dd91de6c1c5aa1ef8a7875e3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sat, 18 Apr 2026 11:49:27 -0400 Subject: [PATCH 628/697] scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd() commit 778c2ab142c625a8a8afa570e0f9b7873f445d99 upstream. Two latent bugs in the Text-phase handler, both present since the original LIO integration in commit e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1"): 1) DataDigest CRC buffer overread (4 bytes past text_in). text_in is kzalloc()'d at ALIGN(payload_length, 4). rx_size is then incremented by ISCSI_CRC_LEN to make room for the received DataDigest in the iovec, but the same (now-bumped) rx_size is passed as the buffer length to iscsit_crc_buf(): if (conn->conn_ops->DataDigest) { ... rx_size += ISCSI_CRC_LEN; } ... if (conn->conn_ops->DataDigest) { data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); iscsit_crc_buf() walks rx_size bytes of text_in with crc32c(), so when DataDigest is negotiated it reads 4 bytes past the end of the text_in allocation. KASAN reproduces this directly on the unpatched mainline tree as slab-out-of-bounds in crc32c() called from the Text PDU path. The OOB bytes feed crc32c() and are then compared against the initiator-supplied checksum, so the value does not flow back to the attacker, but the kernel does read past the buffer on every Text PDU with DataDigest=CRC32C. Fix by passing the actual padded payload length (ALIGN(payload_length, 4)) that was used for the kzalloc(). 2) Stale cmd->text_in_ptr re-free (double-free) on ERL>0 bad DataDigest drop. On DataDigest mismatch with ErrorRecoveryLevel > 0 the handler silently drops the PDU and lets the initiator plug the CmdSN gap: kfree(text_in); return 0; cmd->text_in_ptr still points at the freed buffer. The next Text Request on the same ITT re-enters iscsit_setup_text_cmd(), which unconditionally does kfree(cmd->text_in_ptr); cmd->text_in_ptr = NULL; freeing the same pointer a second time. Session teardown via iscsit_release_cmd() has the same shape and hits the same double-free if the connection is dropped before a second Text Request arrives. On an unmodified mainline tree the bug-1 CRC overread fires first on the initial valid Text Request and perturbs the subsequent state, so #4 was isolated by building a kernel with only the bug-1 hunk of this patch applied plus temporary printk() observability around the three relevant kfree() sites. The observability prints are not part of this patch. On that build, a three-PDU Text Request sequence after login produces two back-to-back splats: BUG: KASAN: double-free in iscsit_setup_text_cmd+0x?? BUG: KASAN: double-free in iscsit_release_cmd+0x?? showing the same pointer freed in the ERL>0 drop path and again in iscsit_setup_text_cmd() (next Text Request on the same ITT) and once more in iscsit_release_cmd() (session teardown). On distro kernels with CONFIG_SLAB_FREELIST_HARDENED=y (default) the double-free becomes a remote kernel BUG(); on non-hardened kernels it corrupts the slab freelist. Fix by clearing cmd->text_in_ptr after the kfree() in the ERL>0 drop path. With both hunks applied #4 is directly observable on the stock tree without observability printks; fixing bug-1 alone would mask #4 less, not more, so the hunks are submitted together. Both fixes are one-liners. The Text PDU state machine is unchanged and the wire protocol is unaffected. Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Tested-by: John Garry Reviewed-by: John Garry Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index a2dde08c8a62..9d05d014d9bf 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2281,7 +2281,9 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, goto reject; if (conn->conn_ops->DataDigest) { - data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); + data_crc = iscsit_crc_buf(text_in, + ALIGN(payload_length, 4), + 0, NULL); if (checksum != data_crc) { pr_err("Text data CRC32C DataDigest" " 0x%08x does not match computed" @@ -2300,6 +2302,7 @@ iscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, " Command CmdSN: 0x%08x due to" " DataCRC error.\n", hdr->cmdsn); kfree(text_in); + cmd->text_in_ptr = NULL; return 0; } } else { From 594a40360012ce5f94c715d5e3b20fa3af7d525a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 11 May 2026 14:49:14 -0400 Subject: [PATCH 629/697] scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf commit bf33e01f88388c43e285492a63e539df6ffed64c upstream. iscsi_encode_text_output() concatenates "key=value\0" records into login->rsp_buf, an 8192-byte kzalloc(MAX_KEY_VALUE_PAIRS) buffer allocated in iscsit_alloc_login_setup_buffer(). The three sprintf() call sites in this function (lines 1398, 1411, 1424 in v7.1-rc2) never check the remaining buffer capacity: *length += sprintf(output_buf, "%s=%s", er->key, er->value); *length += 1; output_buf = textbuf + *length; The 8192-byte ceiling at iscsi_target_check_login_request() bounds the *input* Login PDU payload, but a single PDU can carry up to 2048 minimal four-byte "a=b\0" pairs, each unknown key expanding to a 16-byte "a=NotUnderstood\0" output record via iscsi_add_notunderstood_response(). 2048 * 16 = 32 KiB of output into an 8 KiB buffer, producing a ~24 KiB heap overrun in the kmalloc-8k slab. The fix introduces a static iscsi_encode_text_record() helper that uses snprintf() with a per-call bounds check against the remaining buffer, and threads a u32 textbuf_size parameter through iscsi_encode_text_output(). Both call sites in iscsi_target_handle_csg_zero() (PHASE_SECURITY) and iscsi_target_handle_csg_one() (PHASE_OPERATIONAL) pass MAX_KEY_VALUE_PAIRS. On overflow the encoder logs the condition, calls iscsi_release_extra_responses() to drop queued records, and returns -1; both caller sites now emit ISCSI_STATUS_CLS_INITIATOR_ERR / ISCSI_LOGIN_STATUS_INIT_ERR via iscsit_tx_login_rsp() before returning, so the initiator sees an explicit failed-login response rather than a silent connection drop. (Prior to this patch only the PHASE_OPERATIONAL caller did that; the PHASE_SECURITY caller is converted to the same shape.) Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Tested-by: John Garry Reviewed-by: John Garry Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target_nego.c | 7 ++- .../target/iscsi/iscsi_target_parameters.c | 62 ++++++++++++++----- .../target/iscsi/iscsi_target_parameters.h | 2 +- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index 832588f21f91..b03ed154ca34 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -899,10 +899,14 @@ static int iscsi_target_handle_csg_zero( SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); - if (ret < 0) + if (ret < 0) { + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, + ISCSI_LOGIN_STATUS_INIT_ERR); return -1; + } if (!iscsi_check_negotiated_keys(conn->param_list)) { bool auth_required = iscsi_conn_auth_required(conn); @@ -986,6 +990,7 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo SENDER_TARGET, login->rsp_buf, &login->rsp_length, + MAX_KEY_VALUE_PAIRS, conn->param_list, conn->tpg->tpg_attrib.login_keys_workaround); if (ret < 0) { diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index 1d4e1788e073..14eef580565a 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1371,19 +1371,42 @@ int iscsi_decode_text_input( return -1; } +/* + * Append "key=value" plus a trailing NUL into @textbuf at *@length. + * Returns 0 on success and advances *@length, or -EMSGSIZE if the + * record (including the NUL) would not fit in the remaining buffer. + */ +static int iscsi_encode_text_record(char *textbuf, u32 *length, + u32 textbuf_size, + const char *key, const char *value) +{ + int n; + u32 avail; + + if (*length >= textbuf_size) + return -EMSGSIZE; + + avail = textbuf_size - *length; + n = snprintf(textbuf + *length, avail, "%s=%s", key, value); + if (n < 0 || (u32)n + 1 > avail) + return -EMSGSIZE; + + *length += n + 1; + return 0; +} + int iscsi_encode_text_output( u8 phase, u8 sender, char *textbuf, u32 *length, + u32 textbuf_size, struct iscsi_param_list *param_list, bool keys_workaround) { - char *output_buf = NULL; struct iscsi_extra_response *er; struct iscsi_param *param; - - output_buf = textbuf + *length; + int ret; if (iscsi_enforce_integrity_rules(phase, param_list) < 0) return -1; @@ -1395,10 +1418,12 @@ int iscsi_encode_text_output( !IS_PSTATE_RESPONSE_SENT(param) && !IS_PSTATE_REPLY_OPTIONAL(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_RESPONSE_SENT(param); pr_debug("Sending key: %s=%s\n", param->name, param->value); @@ -1408,10 +1433,12 @@ int iscsi_encode_text_output( !IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param) && (param->phase & phase)) { - *length += sprintf(output_buf, "%s=%s", - param->name, param->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, + textbuf_size, + param->name, + param->value); + if (ret < 0) + goto err_overflow; SET_PSTATE_PROPOSER(param); iscsi_check_proposer_for_optional_reply(param, keys_workaround); @@ -1421,14 +1448,21 @@ int iscsi_encode_text_output( } list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { - *length += sprintf(output_buf, "%s=%s", er->key, er->value); - *length += 1; - output_buf = textbuf + *length; + ret = iscsi_encode_text_record(textbuf, length, textbuf_size, + er->key, er->value); + if (ret < 0) + goto err_overflow; pr_debug("Sending key: %s=%s\n", er->key, er->value); } iscsi_release_extra_responses(param_list); return 0; + +err_overflow: + pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n", + textbuf_size); + iscsi_release_extra_responses(param_list); + return -1; } int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h index c672a971fcb7..38d2238dfe08 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.h +++ b/drivers/target/iscsi/iscsi_target_parameters.h @@ -43,7 +43,7 @@ extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_ extern int iscsi_extract_key_value(char *, char **, char **); extern int iscsi_update_param_value(struct iscsi_param *, char *); extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsit_conn *); -extern int iscsi_encode_text_output(u8, u8, char *, u32 *, +extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32, struct iscsi_param_list *, bool); extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, From 4a3a19c98a8207ad08bec554703d90f2c34a8cc6 Mon Sep 17 00:00:00 2001 From: Alexandru Hossu Date: Thu, 21 May 2026 17:11:21 +0200 Subject: [PATCH 630/697] scsi: target: iscsi: Validate CHAP_R length before base64 decode commit 85db7391310b1304d2dc8ae3b0b12105a9567147 upstream. chap_server_compute_hash() allocates client_digest as kzalloc(chap->digest_size) and then, for BASE64-encoded responses, passes chap_r directly to chap_base64_decode() without checking whether the input length could produce more than digest_size bytes of output. chap_base64_decode() writes to the destination unconditionally as long as there is input to consume. With MAX_RESPONSE_LENGTH set to 128 and the "0b" prefix stripped by extract_param(), up to 127 base64 characters can reach the decoder. 127 characters decode to 95 bytes. For SHA-256 (digest_size=32) this overflows client_digest by 63 bytes; for MD5 (digest_size=16) the overflow is 79 bytes. The length check at line 344 fires after the write has already happened. The HEX branch in the same switch statement already validates the length up front. Apply the same approach to the BASE64 branch: strip trailing base64 padding characters, then reject any input whose data length exceeds DIV_ROUND_UP(digest_size * 4, 3) before calling the decoder. Stripping trailing '=' before the comparison handles both padded and unpadded encodings. chap_base64_decode() already returns early on '=', so the full original string is still passed to the decoder unchanged. The mutual CHAP path decodes CHAP_C into initiatorchg_binhex, which is kzalloc(CHAP_CHALLENGE_STR_LEN). extract_param() caps initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so at most CHAP_CHALLENGE_STR_LEN-1 base64 characters reach the decoder. The maximum decoded size, DIV_ROUND_UP((CHAP_CHALLENGE_STR_LEN-1) * 3, 4), is less than CHAP_CHALLENGE_STR_LEN, so no overflow is possible there. A comment is added at the call site to document this. Fixes: 1e5733883421 ("scsi: target: iscsi: Support base64 in CHAP") Cc: stable@vger.kernel.org Signed-off-by: Alexandru Hossu Reviewed-by: David Disseldorp Link: https://patch.msgid.link/20260521151121.808477-1-hossu.alexandru@gmail.com Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/target/iscsi/iscsi_target_auth.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index c8a248bd11be..02a4c9aff98d 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -339,13 +339,22 @@ static int chap_server_compute_hash( goto out; } break; - case BASE64: + case BASE64: { + size_t r_len = strlen(chap_r); + + while (r_len > 0 && chap_r[r_len - 1] == '=') + r_len--; + if (r_len > DIV_ROUND_UP(chap->digest_size * 4, 3)) { + pr_err("Malformed CHAP_R: base64 payload too long\n"); + goto out; + } if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != chap->digest_size) { pr_err("Malformed CHAP_R: invalid BASE64\n"); goto out; } break; + } default: pr_err("Could not find CHAP_R\n"); goto out; @@ -472,6 +481,14 @@ static int chap_server_compute_hash( } break; case BASE64: + /* + * No overflow check needed: initiatorchg_binhex is + * CHAP_CHALLENGE_STR_LEN bytes and extract_param() caps + * initiatorchg at CHAP_CHALLENGE_STR_LEN characters, so + * the decoded output is at most DIV_ROUND_UP( + * (CHAP_CHALLENGE_STR_LEN - 1) * 3, 4) bytes, which is + * less than CHAP_CHALLENGE_STR_LEN. + */ initiatorchg_len = chap_base64_decode(initiatorchg_binhex, initiatorchg, strlen(initiatorchg)); From 9c698b2c43c2667c34f5336bf46ad5786216ac2a Mon Sep 17 00:00:00 2001 From: Berkant Koc Date: Tue, 19 May 2026 22:08:17 +0200 Subject: [PATCH 631/697] drm/hyperv: validate resolution_count and fix WIN8 fallback commit 13d33b9ef67066c77c84273fac5a1d3fde3533d1 upstream. A SYNTHVID_RESOLUTION_RESPONSE with resolution_count > 64 walks past the supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT] array in the parse loop. Bound resolution_count against the array size, folded into the existing zero-check. When the WIN10 resolution probe fails, the caller in hyperv_connect_vsp() left hv->screen_*_max / preferred_* unpopulated, which sets mode_config.max_width / max_height to 0 and makes drm_internal_framebuffer_create() reject every userspace framebuffer with -EINVAL. The pre-WIN10 branch had the same gap for preferred_width / preferred_height. Use a single post-probe fallback guarded by screen_width_max == 0 so both paths converge on the WIN8 defaults. Signed-off-by: Berkant Koc Assisted-by: Claude:claude-opus-4-7 berkoc-pipeline Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device") Cc: stable@vger.kernel.org # 5.14+ Reviewed-by: Michael Kelley Tested-by: Michael Kelley Signed-off-by: Hamza Mahfooz Link: https://patch.msgid.link/6945b22419c7d404b4954a113de2ac9c900dba93.1779542874.git.me@berkoc.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 013a7829182d..db2c1efbfb05 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -396,8 +396,11 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return -ETIMEDOUT; } - if (msg->resolution_resp.resolution_count == 0) { - drm_err(dev, "No supported resolutions\n"); + if (msg->resolution_resp.resolution_count == 0 || + msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err(dev, "Invalid resolution count: %d\n", + msg->resolution_resp.resolution_count); return -ENODEV; } @@ -513,9 +516,13 @@ int hyperv_connect_vsp(struct hv_device *hdev) ret = hyperv_get_supported_resolution(hdev); if (ret) drm_err(dev, "Failed to get supported resolution from host, use default\n"); - } else { + } + + if (!hv->screen_width_max) { hv->screen_width_max = SYNTHVID_WIDTH_WIN8; hv->screen_height_max = SYNTHVID_HEIGHT_WIN8; + hv->preferred_width = SYNTHVID_WIDTH_WIN8; + hv->preferred_height = SYNTHVID_HEIGHT_WIN8; } hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes; From 164dc7bf17609340233c6bf4f66bb7c7008a0511 Mon Sep 17 00:00:00 2001 From: Berkant Koc Date: Sat, 23 May 2026 15:27:47 +0200 Subject: [PATCH 632/697] drm/hyperv: validate VMBus packet size in receive callback commit 7f87763f47a3c22fb50265a00619ef10f2394b18 upstream. hyperv_receive_sub() reads msg->vid_hdr.type and dispatches into one of four message-type branches without knowing how many bytes the host wrote into hv->recv_buf. The completion path then runs memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE), so the consumer that wakes on wait_for_completion_timeout() can read up to 16 KiB of residue from a prior message as if it were the response payload. Pass bytes_recvd into hyperv_receive_sub() and reject any packet that does not cover the pipe + synthvid header. A single switch on msg->vid_hdr.type then computes the type-specific payload size: the three completion-driving types (SYNTHVID_VERSION_RESPONSE, SYNTHVID_RESOLUTION_RESPONSE, SYNTHVID_VRAM_LOCATION_ACK) fall through to a shared exit that requires that size before memcpy/complete, while SYNTHVID_FEATURE_CHANGE validates its own payload and returns before reading is_dirt_needed. Unknown types are dropped. SYNTHVID_RESOLUTION_RESPONSE is variable length: the host fills resolution_count entries, not the full SYNTHVID_MAX_RESOLUTION_COUNT array. Validate the fixed prefix first so resolution_count can be read, bound it against the array, then require only the count-sized array, so the shorter responses the host actually sends are accepted. Only run the sub-handler when vmbus_recvpacket() returned success. The memcpy length is bytes_recvd, which is bounded by VMBUS_MAX_PACKET_SIZE only on a successful receive; on -ENOBUFS vmbus_recvpacket() instead reports the required length, which can exceed hv->recv_buf, so copying bytes_recvd would read and write past the 16 KiB buffers. Gating on the success return keeps the copy bounded. The nonzero-return path is itself a malformed-message case and is now logged rather than silently skipped; channel recovery is not attempted. Rejected packets are reported via drm_err_ratelimited() rather than silently dropped, matching the CoCo-hardened pattern in hv_kvp_onchannelcallback(). Fixes: 76c56a5affeb ("drm/hyperv: Add DRM driver for hyperv synthetic video device") Cc: stable@vger.kernel.org # 5.14+ Signed-off-by: Berkant Koc Assisted-by: Claude:claude-opus-4-7 berkoc-pipeline Reviewed-by: Michael Kelley Tested-by: Michael Kelley Signed-off-by: Hamza Mahfooz Link: https://patch.msgid.link/8200dbc199c7a9b75ac7e8af6c748d2189b5ebd5.1779542874.git.me@berkoc.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/hyperv/hyperv_drm_proto.c | 100 +++++++++++++++++++--- 1 file changed, 87 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index db2c1efbfb05..5ef76b926a32 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -425,30 +425,92 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return 0; } -static void hyperv_receive_sub(struct hv_device *hdev) +static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) { struct hyperv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg; + size_t hdr_size; + size_t need; if (!hv) return; - msg = (struct synthvid_msg *)hv->recv_buf; - - /* Complete the wait event */ - if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || - msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { - memcpy(hv->init_buf, msg, VMBUS_MAX_PACKET_SIZE); - complete(&hv->wait); + hdr_size = sizeof(struct pipe_msg_hdr) + + sizeof(struct synthvid_msg_hdr); + if (bytes_recvd < hdr_size) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for header: %u\n", + bytes_recvd); return; } - if (msg->vid_hdr.type == SYNTHVID_FEATURE_CHANGE) { + msg = (struct synthvid_msg *)hv->recv_buf; + need = hdr_size; + + switch (msg->vid_hdr.type) { + case SYNTHVID_VERSION_RESPONSE: + need += sizeof(struct synthvid_version_resp); + break; + case SYNTHVID_RESOLUTION_RESPONSE: + /* + * The resolution response is variable length: the host + * fills resolution_count entries, not the full + * SYNTHVID_MAX_RESOLUTION_COUNT array. Require the fixed + * prefix first so resolution_count can be read, then + * demand exactly the count-sized array. + */ + need += offsetof(struct synthvid_supported_resolution_resp, + supported_resolution); + if (bytes_recvd < need) + break; + if (msg->resolution_resp.resolution_count > + SYNTHVID_MAX_RESOLUTION_COUNT) { + drm_err_ratelimited(&hv->dev, + "synthvid resolution count too large: %u\n", + msg->resolution_resp.resolution_count); + return; + } + need += msg->resolution_resp.resolution_count * + sizeof(struct hvd_screen_info); + break; + case SYNTHVID_VRAM_LOCATION_ACK: + need += sizeof(struct synthvid_vram_location_ack); + break; + case SYNTHVID_FEATURE_CHANGE: + /* + * Not a completion-driving message: validate its own payload + * and consume it here rather than falling through to the + * memcpy/complete shared by the wait-event responses. + */ + if (bytes_recvd < need + + sizeof(struct synthvid_feature_change)) { + drm_err_ratelimited(&hv->dev, + "synthvid feature change packet too small: %u\n", + bytes_recvd); + return; + } hv->dirt_needed = msg->feature_chg.is_dirt_needed; if (hv->dirt_needed) hyperv_hide_hw_ptr(hv->hdev); + return; + default: + return; + } + + /* + * Shared completion path for the wait-event responses + * (VERSION_RESPONSE, RESOLUTION_RESPONSE, VRAM_LOCATION_ACK): + * require the type-specific payload before handing the buffer to + * the waiter. + */ + if (bytes_recvd < need) { + drm_err_ratelimited(&hv->dev, + "synthvid packet too small for type %u: %u < %zu\n", + msg->vid_hdr.type, bytes_recvd, need); + return; } + memcpy(hv->init_buf, msg, bytes_recvd); + complete(&hv->wait); } static void hyperv_receive(void *ctx) @@ -469,9 +531,21 @@ static void hyperv_receive(void *ctx) ret = vmbus_recvpacket(hdev->channel, recv_buf, VMBUS_MAX_PACKET_SIZE, &bytes_recvd, &req_id); - if (bytes_recvd > 0 && - recv_buf->pipe_hdr.type == PIPE_MSG_DATA) - hyperv_receive_sub(hdev); + if (ret) { + /* + * A nonzero return (e.g. -ENOBUFS for an oversized + * packet) is itself a malformed message: bytes_recvd + * then reports the required length rather than a copied + * payload, so it must not be forwarded to the + * sub-handler. Channel recovery is not attempted. + */ + drm_err_ratelimited(&hv->dev, + "vmbus_recvpacket failed: %d (need %u)\n", + ret, bytes_recvd); + } else if (bytes_recvd > 0 && + recv_buf->pipe_hdr.type == PIPE_MSG_DATA) { + hyperv_receive_sub(hdev, bytes_recvd); + } } while (bytes_recvd > 0 && ret == 0); } From 0dfa42cfe4dbe114533480503934f43e33c1e83d Mon Sep 17 00:00:00 2001 From: Zhenghang Xiao Date: Tue, 26 May 2026 16:53:13 +0800 Subject: [PATCH 633/697] drm/gem: fix race between change_handle and handle_delete commit 7164d78559b0ff29931a366a840a9e5dd53d4b7c upstream. drm_gem_change_handle_ioctl leaves the old handle live in the IDR during the window between spin_unlock(table_lock) and the final spin_lock(table_lock). A concurrent drm_gem_handle_delete on the old handle succeeds in this window, decrements handle_count to 0, and frees the GEM object while the new handle's IDR entry still references it. NULL the old handle's IDR entry before dropping table_lock so that any concurrent GEM_CLOSE on the old handle sees NULL and returns -EINVAL. Restore the old entry on the prime-bookkeeping error path. Fixes: 5e28b7b94408 ("drm: Set old handle to NULL before prime swap in change_handle") Signed-off-by: Zhenghang Xiao Cc: stable@vger.kernel.org Signed-off-by: Dave Airlie Link: https://patch.msgid.link/20260526085313.26791-1-kipreyyy@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_gem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index cc34ee8e1d48..d9f861de2df3 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -1015,6 +1015,7 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, goto out_unlock; } + idr_replace(&file_priv->object_idr, NULL, args->handle); spin_unlock(&file_priv->table_lock); if (obj->dma_buf) { @@ -1023,6 +1024,7 @@ int drm_gem_change_handle_ioctl(struct drm_device *dev, void *data, if (ret < 0) { spin_lock(&file_priv->table_lock); idr_remove(&file_priv->object_idr, handle); + idr_replace(&file_priv->object_idr, obj, args->handle); spin_unlock(&file_priv->table_lock); goto out_unlock; } From fed64e47a32f390fb3a0fc7c87b8ef96c8897333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Wed, 20 May 2026 13:49:43 +0300 Subject: [PATCH 634/697] drm/i915/psr: Block DC states on vblank enable when Panel Replay supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8bb9093df555f9e89fdbe1405118b11384c03e04 upstream. Currently we are blocking DC states only when Panel Replay is enabled on vblank enable. It may happen that Panel Replay is getting enabled when vblank is already enabled. Fix this by blocking DC states always if Panel Replay is supported. While at it take care of possible dual eDP case by looping all encoders supporting PSR. Fixes: 0c427ac78a1d ("drm/i915/psr: Add interface to notify PSR of vblank enable/disable") Cc: # v6.16+ Signed-off-by: Jouni Högander Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260520104944.239797-1-jouni.hogander@intel.com (cherry picked from commit eb5911f990554f7ce947dd53df00c114362e4465) Signed-off-by: Tvrtko Ursulin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/display/intel_psr.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index eed281b93cd8..0f791d0a5e0a 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -3982,32 +3982,33 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, bool enable) { struct intel_encoder *encoder; + bool block_dc_states = false; for_each_intel_encoder_with_psr(display->drm, encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); mutex_lock(&intel_dp->psr.lock); - if (intel_dp->psr.panel_replay_enabled) { - mutex_unlock(&intel_dp->psr.lock); - break; - } + if (CAN_PANEL_REPLAY(intel_dp)) + block_dc_states = true; - if (intel_dp->psr.enabled && intel_dp->psr.pkg_c_latency_used) + if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && + intel_dp->psr.pkg_c_latency_used) intel_psr_apply_underrun_on_idle_wa_locked(intel_dp); mutex_unlock(&intel_dp->psr.lock); - return; } /* * NOTE: intel_display_power_set_target_dc_state is used - * only by PSR * code for DC3CO handling. DC3CO target + * only by PSR code for DC3CO handling. DC3CO target * state is currently disabled in * PSR code. If DC3CO * is taken into use we need take that into account here * as well. */ - intel_display_power_set_target_dc_state(display, enable ? DC_STATE_DISABLE : - DC_STATE_EN_UPTO_DC6); + if (block_dc_states) + intel_display_power_set_target_dc_state(display, enable ? + DC_STATE_DISABLE : + DC_STATE_EN_UPTO_DC6); } static void From c9ae7e7e3bc98615364313b08d7acea5239ded0b Mon Sep 17 00:00:00 2001 From: Janusz Krzysztofik Date: Fri, 8 May 2026 14:23:51 +0200 Subject: [PATCH 635/697] drm/i915: Fix potential UAF in TTM object purge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5c4063c87a619e4df954c179d24628636f5db15f upstream. TLDR: The bo->ttm object might be changed by calling ttm_bo_validate(), move casting it to an i915_tt object later to actually get the right pointer. A user reported hitting the following bug under heavy use on DG2: [26620.095550] Oops: general protection fault, probably for non-canonical address 0xa56b6b6b6b6b6b8b: 0000 1 SMP NOPTI [26620.095556] CPU: 2 UID: 0 PID: 631 Comm: Xorg Not tainted 6.18.8 #1 PREEMPT(lazy) [26620.095558] Hardware name: ASRock B850M Steel Legend WiFi/B850M Steel Legend WiFi, BIOS 3.50 09/18/2025 [26620.095559] RIP: 0010:i915_ttm_purge+0x84/0x100 [i915] [26620.095604] Code: 00 00 00 48 8d 54 24 10 48 89 e6 48 89 fb e8 83 aa ae ff 85 c0 75 6f 48 83 bb a8 01 00 00 00 74 2c 48 8b 45 78 48 85 c0 74 23 <48> 8b 78 20 48 c7 c2 ff ff ff ff 31 f6 e8 7a 73 e3 e0 48 8b 7d 78 [26620.095605] RSP: 0018:ffffc90005fd7430 EFLAGS: 00010282 [26620.095607] RAX: a56b6b6b6b6b6b6b RBX: ffff8881f46c3dc0 RCX: 0000000000000000 [26620.095608] RDX: 0000000000000000 RSI: 0000000000000246 RDI: 00000000ffffffff [26620.095609] RBP: ffff888289610f00 R08: 0000000000000001 R09: ffff88823b022000 [26620.095609] R10: ffff888103029b28 R11: ffff8881fc7f3800 R12: ffff88810b6150d0 [26620.095609] R13: ffff888289610f00 R14: 0000000000000000 R15: ffff8881f46c3dc0 [26620.095610] FS: 00007f1004d86900(0000) GS:ffff88901c858000(0000) knlGS:0000000000000000 [26620.095611] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [26620.095611] CR2: 00007f0fdf489000 CR3: 000000035b0c1000 CR4: 0000000000750ef0 [26620.095612] PKRU: 55555554 [26620.095612] Call Trace: [26620.095615] [26620.095615] i915_ttm_move+0x2b9/0x420 [i915] [26620.095642] ? ttm_tt_init+0x65/0x80 [ttm] [26620.095644] ? i915_ttm_tt_create+0xc6/0x150 [i915] [26620.095667] ttm_bo_handle_move_mem+0xb6/0x160 [ttm] [26620.095669] ttm_bo_evict+0x100/0x150 [ttm] [26620.095671] ? preempt_count_add+0x64/0xa0 [26620.095673] ? _raw_spin_lock+0xe/0x30 [26620.095675] ? _raw_spin_unlock+0xd/0x30 [26620.095675] ? i915_gem_object_evictable+0xb7/0xd0 [i915] [26620.095704] ttm_bo_evict_cb+0x6e/0xd0 [ttm] [26620.095705] ttm_lru_walk_for_evict+0xa6/0x200 [ttm] [26620.095708] ttm_bo_alloc_resource+0x185/0x4f0 [ttm] [26620.095709] ? init_object+0x62/0xd0 [26620.095712] ttm_bo_validate+0x7a/0x180 [ttm] [26620.095713] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095714] __i915_ttm_get_pages+0xb0/0x170 [i915] [26620.095737] i915_ttm_get_pages+0x9f/0x150 [i915] [26620.095759] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095786] ? alloc_debug_processing+0xd0/0x100 [26620.095787] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095788] ? i915_vma_instance+0xa0/0x4e0 [i915] [26620.095822] __i915_gem_object_get_pages+0x2f/0x40 [i915] [26620.095848] i915_vma_pin_ww+0x706/0x980 [i915] [26620.095875] ? i915_gem_do_execbuffer+0xedc/0x2b40 [i915] [26620.095904] eb_validate_vmas+0x170/0xa00 [i915] [26620.095930] i915_gem_do_execbuffer+0x1201/0x2b40 [i915] [26620.095953] ? alloc_debug_processing+0xd0/0x100 [26620.095954] ? _raw_spin_unlock_irqrestore+0x16/0x30 [26620.095955] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.095977] ? __wake_up_sync_key+0x32/0x50 [26620.095979] ? i915_gem_execbuffer2_ioctl+0xc9/0x240 [i915] [26620.096001] ? __slab_alloc.isra.0+0x67/0xc0 [26620.096003] i915_gem_execbuffer2_ioctl+0x11a/0x240 [i915] Results from decode_stacktrace.sh pointed to dereference of a file pointer field of a i915 TTM page vector container associated with an object being purged on eviction. That path is taken when the object is marked as no longer needed. Code analysis revealed a possibility of the i915 TTM page vector container being replaced with a new instance inside a function that purges content of the object, should it be still busy. That function is called, indirectly via a more general function that changes the object's placement and caching policy, before the problematic dereference, but still after a pointer to the container is captured, rendering the pointer no longer valid. Fix the issue by capturing the pointer to the container only after its potential replacement. v2: Move the container_of() inside the if block (Sebastian), - a simplified version of the commit description that explains briefly why the change is necessary (Christian). Closes: https://gitlab.freedesktop.org/drm/i915/kernel/-/work_items/14882 Fixes: 7ae034590ceae ("drm/i915/ttm: add tt shmem backend") Signed-off-by: Janusz Krzysztofik Cc: stable@vger.kernel.org # v5.17+ Cc: Matthew Auld Cc: Thomas Hellström Cc: Sebastian Brzezinka Cc: Christian König Reviewed-by: Andi Shyti Reviewed-by: Christian König Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260508122612.469227-2-janusz.krzysztofik@linux.intel.com (cherry picked from commit 4462966a93eb185849b7f174f0d0de53476d00a4) Signed-off-by: Tvrtko Ursulin Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 1f4814968868..3d53c54eb48f 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -416,8 +416,6 @@ void i915_ttm_free_cached_io_rsgt(struct drm_i915_gem_object *obj) int i915_ttm_purge(struct drm_i915_gem_object *obj) { struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); - struct i915_ttm_tt *i915_tt = - container_of(bo->ttm, typeof(*i915_tt), ttm); struct ttm_operation_ctx ctx = { .interruptible = true, .no_wait_gpu = false, @@ -432,16 +430,22 @@ int i915_ttm_purge(struct drm_i915_gem_object *obj) if (ret) return ret; - if (bo->ttm && i915_tt->filp) { - /* - * The below fput(which eventually calls shmem_truncate) might - * be delayed by worker, so when directly called to purge the - * pages(like by the shrinker) we should try to be more - * aggressive and release the pages immediately. - */ - shmem_truncate_range(file_inode(i915_tt->filp), - 0, (loff_t)-1); - fput(fetch_and_zero(&i915_tt->filp)); + if (bo->ttm) { + struct i915_ttm_tt *i915_tt = + container_of(bo->ttm, typeof(*i915_tt), ttm); + + if (i915_tt->filp) { + /* + * The below fput(which eventually calls shmem_truncate) + * might be delayed by worker, so when directly called + * to purge the pages(like by the shrinker) we should + * try to be more aggressive and release the pages + * immediately. + */ + shmem_truncate_range(file_inode(i915_tt->filp), + 0, (loff_t)-1); + fput(fetch_and_zero(&i915_tt->filp)); + } } obj->write_domain = 0; From ffa7dce35b64fc5cfce56fe9f164c708a6b5ca54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timur=20Krist=C3=B3f?= Date: Tue, 19 May 2026 10:41:54 +0200 Subject: [PATCH 636/697] drm/amd/pm/si: Disregard vblank time when no displays are connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit dd4f3ee535b3b0ac027f75dbf9dc5fc88733c765 upstream. When no displays are connected, there is no vblank happening so the power management code shouldn't worry about it. This fixes a regression that caused the memory clock to be stuck at maximum when there were no displays connected to a SI GPU. Fixes: 9003a0746864 ("drm/amd/pm: Treat zero vblank time as too short in si_dpm (v3)") Fixes: 9d73b107a61b ("drm/amd/pm: Use pm_display_cfg in legacy DPM (v2)") Reviewed-by: Alex Deucher Tested-by: Jeremy Klarenbeek Signed-off-by: Timur Kristóf Signed-off-by: Alex Deucher (cherry picked from commit 6d87e0199f7b83735b56e422d59f170a201897a8) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index c25e72e31d8b..0385b24e7e4a 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3081,6 +3081,10 @@ static bool si_dpm_vblank_too_short(void *handle) /* we never hit the non-gddr5 limit so disable it */ u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0; + /* Disregard vblank time when there are no displays connected */ + if (!adev->pm.pm_display_cfg.num_display) + return false; + /* Consider zero vblank time too short and disable MCLK switching. * Note that the vblank time is set to maximum when no displays are attached, * so we'll still enable MCLK switching in that case. From 9a91692fae5cac5662b962c5d8f6f60fb02b08b7 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 12 May 2026 15:56:57 +0900 Subject: [PATCH 637/697] serial: altera_jtaguart: handle uart_add_one_port() failures commit ea66be25f0e934f49d24cd0c5845d13cdba3520b upstream. altera_jtaguart_probe() maps the register window before registering the UART port, but it ignores failures from uart_add_one_port(). If port registration fails, probe still returns success and the mapping remains live until a later remove path that is not part of probe failure cleanup. Return the uart_add_one_port() error and unmap the register window on that failure path. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 5bcd601049c6 ("serial: Add driver for the Altera JTAG UART") Cc: stable Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Link: https://patch.msgid.link/20260512065837.79528-1-mhun512@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_jtaguart.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/altera_jtaguart.c b/drivers/tty/serial/altera_jtaguart.c index d47a62d1c9f7..20f079fe11d8 100644 --- a/drivers/tty/serial/altera_jtaguart.c +++ b/drivers/tty/serial/altera_jtaguart.c @@ -379,6 +379,7 @@ static int altera_jtaguart_probe(struct platform_device *pdev) struct resource *res_mem; int i = pdev->id; int irq; + int ret; /* -1 emphasizes that the platform must have one port, no .N suffix */ if (i == -1) @@ -418,7 +419,11 @@ static int altera_jtaguart_probe(struct platform_device *pdev) port->flags = UPF_BOOT_AUTOCONF; port->dev = &pdev->dev; - uart_add_one_port(&altera_jtaguart_driver, port); + ret = uart_add_one_port(&altera_jtaguart_driver, port); + if (ret) { + iounmap(port->membase); + return ret; + } return 0; } From 78d0d6f69bd6a876602683406dc945056bbf3e1c Mon Sep 17 00:00:00 2001 From: Prasanna S Date: Tue, 28 Apr 2026 09:56:13 +0530 Subject: [PATCH 638/697] serial: qcom-geni: fix UART_RX_PAR_EN bit position commit ca2584d841b69391ffc4144840563d2e1a0018df upstream. UART_RX_PAR_EN is incorrectly defined as bit 3, which triggers false framing errors (S_GP_IRQ_1_EN) and causes received data to be dropped when parity is enabled and the parity bit is 0. Define UART_RX_PAR_EN as bit 4 of the SE_UART_RX_TRANS_CFG register, as specified in the reference manual. Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") Cc: stable Signed-off-by: Prasanna S Reviewed-by: Konrad Dybcio Link: https://patch.msgid.link/20260428-serial-bit-correct-v1-1-9131ad5b97d8@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 8058b839b26c..eed56d780908 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -49,7 +49,7 @@ #define TX_STOP_BIT_LEN_2 2 /* SE_UART_RX_TRANS_CFG */ -#define UART_RX_PAR_EN BIT(3) +#define UART_RX_PAR_EN BIT(4) /* SE_UART_RX_WORD_LEN */ #define RX_WORD_LEN_MASK GENMASK(9, 0) From 654f45a8569f3cd6ff20bd724a18e0cce65893ba Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Wed, 6 May 2026 10:15:21 +0530 Subject: [PATCH 639/697] serial: qcom_geni: fix kfifo underflow when flush precedes DMA completion IRQ commit 452d6fa37ae9b021f4f6d397dbae077f7296f6f4 upstream. When uart_flush_buffer() runs before the DMA completion IRQ is delivered, the following race can occur (all steps serialized by uart_port_lock): 1. DMA starts: tx_remaining = N, kfifo contains N bytes 2. DMA completes in hardware; IRQ is pending but not yet delivered 3. uart_flush_buffer() acquires the port lock and calls kfifo_reset(), making kfifo_len() = 0 while tx_remaining remains N 4. uart_flush_buffer() releases the port lock 5. DMA IRQ fires; handle_tx_dma() acquires the port lock and calls uart_xmit_advance(uport, tx_remaining) on an empty kfifo uart_xmit_advance() increments kfifo->out by tx_remaining. Since kfifo_reset() already set both in and out to 0, out wraps past in, causing kfifo_len() to return UART_XMIT_SIZE - tx_remaining. The next start_tx_dma() call then submits a DMA transfer of stale buffer data. Fix this by snapshotting kfifo_len() at the start of handle_tx_dma() and skipping uart_xmit_advance() when fifo_len < tx_remaining, which indicates the kfifo was reset by a preceding flush. Fixes: 2aaa43c70778 ("tty: serial: qcom-geni-serial: add support for serial engine DMA") Cc: stable Signed-off-by: Viken Dadhaniya Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260506-serial-dma-stale-tx-buf-v1-1-e3ccb360d719@oss.qualcomm.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/qcom_geni_serial.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index eed56d780908..337fa3261ac7 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1022,8 +1022,20 @@ static void qcom_geni_serial_handle_tx_dma(struct uart_port *uport) { struct qcom_geni_serial_port *port = to_dev_port(uport); struct tty_port *tport = &uport->state->port; + unsigned int fifo_len = kfifo_len(&tport->xmit_fifo); + + /* + * Only advance the kfifo if it still contains the bytes that were + * transferred. uart_flush_buffer() may have run before this IRQ + * fired: it calls kfifo_reset() under the port lock, making + * fifo_len = 0 while tx_remaining remains non-zero. Calling + * uart_xmit_advance() in that case would underflow kfifo->out past + * kfifo->in, making kfifo_len() wrap to UART_XMIT_SIZE - tx_remaining + * and triggering a spurious large DMA transfer of stale data. + */ + if (fifo_len >= port->tx_remaining) + uart_xmit_advance(uport, port->tx_remaining); - uart_xmit_advance(uport, port->tx_remaining); geni_se_tx_dma_unprep(&port->se, port->tx_dma_addr, port->tx_remaining); port->tx_dma_addr = 0; port->tx_remaining = 0; From 10ddd1a320e16f65d899dd16c6bae3f671a2e6e0 Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Tue, 21 Apr 2026 14:57:37 +0800 Subject: [PATCH 640/697] serial: sh-sci: fix memory region release in error path commit 92b1ea22454b08a39baef3a7290fb3ec50366616 upstream. The sci_request_port() function uses request_mem_region() to reserve I/O memory, but in the error path when sci_remap_port() fails, it incorrectly calls release_resource() instead of release_mem_region(). This mismatch can cause resource accounting issues. Fix it by using the correct release function, consistent with sci_release_port(). Fixes: e2651647080930a1 ("serial: sh-sci: Handle port memory region reservations.") Cc: stable Reported-by: kernel test robot Reported-by: Dan Carpenter Closes: https://lore.kernel.org/r/202604032356.SzEjYkBC-lkp@intel.com/ Signed-off-by: Hongling Zeng Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260421065737.724187-1-zenghongling@kylinos.cn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d91b1fb69beb..a64a26fb7ff8 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2845,7 +2845,7 @@ int sci_request_port(struct uart_port *port) ret = sci_remap_port(port); if (unlikely(ret != 0)) { - release_resource(res); + release_mem_region(port->mapbase, sport->reg_size); return ret; } From 8e39badab0904a566bebb32187160895229c5ae9 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Fri, 10 Apr 2026 18:19:31 +0100 Subject: [PATCH 641/697] serial: zs: Fix swapped RI/DSR modem line transition counting commit d15cd40cb1858f75846eaafa9a6bca841b790a92 upstream. Fix a thinko in the status interrupt handler that has caused counters for the RI and DSR modem line transitions to be used for the other line each. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Cc: stable Signed-off-by: Maciej W. Rozycki Link: https://patch.msgid.link/alpine.DEB.2.21.2604101747110.29980@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 79ea7108a0f3..0eaaca0eddc0 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -680,9 +680,9 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uart_handle_dcd_change(uport, zport->mctrl & TIOCM_CAR); if (delta & TIOCM_RNG) - uport->icount.dsr++; - if (delta & TIOCM_DSR) uport->icount.rng++; + if (delta & TIOCM_DSR) + uport->icount.dsr++; if (delta) wake_up_interruptible(&uport->state->port.delta_msr_wait); From 348e01e64a87ce4a84288050584537264e43a5ac Mon Sep 17 00:00:00 2001 From: Shitalkumar Gandhi Date: Mon, 20 Apr 2026 19:29:03 +0530 Subject: [PATCH 642/697] serial: fsl_lpuart: fix rx buffer and DMA map leaks in start_rx_dma commit 9a9254c4a2a3ca2b3da16d173f3b0dd01f397ff6 upstream. lpuart_start_rx_dma() allocates sport->rx_ring.buf with kzalloc() and then maps a scatterlist via dma_map_sg(). On three subsequent error paths the function returns directly without releasing those resources: - when dma_map_sg() returns 0 (-EINVAL): ring->buf is leaked. - when dmaengine_slave_config() fails: ring->buf and the DMA mapping are leaked. - when dmaengine_prep_dma_cyclic() returns NULL: ring->buf and the DMA mapping are leaked. The sole cleanup path, lpuart_dma_rx_free(), is only reached when lpuart_dma_rx_use is set, and the caller lpuart_rx_dma_startup() clears that flag on failure of lpuart_start_rx_dma(). So these resources are permanently leaked on every failure in this function. Repeated port open/close or termios changes under error conditions will slowly consume memory and leave stale streaming DMA mappings behind. Fix it by introducing two error labels that unmap the scatterlist and free the ring buffer as appropriate. While here, replace the misleading -EFAULT (bad userspace pointer) returned when dmaengine_prep_dma_cyclic() fails with the more accurate -ENOMEM, matching how other dmaengine users in the tree treat this failure. No functional change on the success path. Fixes: 5887ad43ee02 ("tty: serial: fsl_lpuart: Use cyclic DMA for Rx") Cc: stable Signed-off-by: Shitalkumar Gandhi Reviewed-by: Frank Li Link: https://patch.msgid.link/20260420135903.2062024-1-shitalkumar.gandhi@cambiumnetworks.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index c9519e649e82..8d218870877f 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1379,7 +1379,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (!nent) { dev_err(sport->port.dev, "DMA Rx mapping error\n"); - return -EINVAL; + ret = -EINVAL; + goto err_free_buf; } dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport); @@ -1391,7 +1392,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) if (ret < 0) { dev_err(sport->port.dev, "DMA Rx slave config failed, err = %d\n", ret); - return ret; + goto err_unmap_sg; } sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan, @@ -1402,7 +1403,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) DMA_PREP_INTERRUPT); if (!sport->dma_rx_desc) { dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n"); - return -EFAULT; + ret = -ENOMEM; + goto err_unmap_sg; } sport->dma_rx_desc->callback = lpuart_dma_rx_complete; @@ -1426,6 +1428,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) } return 0; + +err_unmap_sg: + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); +err_free_buf: + kfree(ring->buf); + ring->buf = NULL; + return ret; } static void lpuart_dma_rx_free(struct uart_port *port) From 2f9c3c161692f5bf1436e869a651bed10936e071 Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Thu, 7 May 2026 15:51:49 -0400 Subject: [PATCH 643/697] drm/amdkfd: fix NULL pointer bug in svm_range_set_attr commit e984d61d92e702096058f0f828f4b2b8563b88ce upstream. The process_info could be NULL if user doesn't call kfd_ioctl_acquire_vm before calling kfd_ioctl_svm. Signed-off-by: Eric Huang Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 83a26c812e0529eb040d31a76f73e33e637243d4) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_svm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index a24b3c00f9f0..04e4bf41ccf7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -3680,6 +3680,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm, svms = &p->svms; + if (!process_info) + return -EINVAL; + mutex_lock(&process_info->lock); svm_range_list_lock_and_flush_work(svms, mm); From 5cf4a41aa0d74e4c83f82d2ce233b5189ed4b43c Mon Sep 17 00:00:00 2001 From: Eric Huang Date: Tue, 12 May 2026 10:19:52 -0400 Subject: [PATCH 644/697] drm/amdkfd: fix a vulnerability of integer overflow in kfd debugger commit 93f5534b35a05ef8a0109c1eefa800062fee810a upstream. get_queue_ids() computes array_size = num_queues * sizeof(uint32_t), which could overflow on 32-bit size_t build. using array_size() instead, it saturates to SIZE_MAX on overflow. Signed-off-by: Eric Huang Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2d57a0475f085c08b49312dfd8edcb461845f285) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 51a47a91e21e..b0466a04ec4b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -3292,12 +3292,14 @@ static void copy_context_work_handler(struct work_struct *work) static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array) { - size_t array_size = num_queues * sizeof(uint32_t); - if (!usr_queue_id_array) return NULL; - return memdup_user(usr_queue_id_array, array_size); + if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) + return ERR_PTR(-EINVAL); + + return memdup_user(usr_queue_id_array, + array_size(num_queues, sizeof(uint32_t))); } int resume_queues(struct kfd_process *p, From 275396bf71c4d30a2a86ccf078f732cc9fe17e26 Mon Sep 17 00:00:00 2001 From: David Francis Date: Thu, 14 May 2026 10:31:20 -0400 Subject: [PATCH 645/697] drm/amdkfd: Check for pdd drm file first in CRIU restore path commit 6842b6a4b72da9b2906ffc5ca9d846ace2c54c14 upstream. CRIU restore ioctls are meant to be called by CRIU with no existing drm file. There's an error path for if the drm file unexpectedly exists. It was positioned so it was missing a fput(drm_file). Do that check earlier, as soon as we have the pdd. Signed-off-by: David Francis Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 2bab781dac78916c5cc8de76345a4102449267d7) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 0fcf150b9ffa..09ebfe8a3062 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -2253,6 +2253,11 @@ static int criu_restore_devices(struct kfd_process *p, ret = -EINVAL; goto exit; } + + if (pdd->drm_file) { + ret = -EINVAL; + goto exit; + } pdd->user_gpu_id = device_buckets[i].user_gpu_id; drm_file = fget(device_buckets[i].drm_fd); @@ -2263,11 +2268,6 @@ static int criu_restore_devices(struct kfd_process *p, goto exit; } - if (pdd->drm_file) { - ret = -EINVAL; - goto exit; - } - /* create the vm using render nodes for kfd pdd */ if (kfd_process_device_init_vm(pdd, drm_file)) { pr_err("could not init vm for given pdd\n"); From 1eb86334e391695d4a40743b114afc15df4dc506 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Sun, 17 May 2026 09:17:42 -0400 Subject: [PATCH 646/697] drm/amdgpu: fix lock leak on ENOMEM in AMDGPU_GEM_OP_GET_MAPPING_INFO commit 2e7f55eb408c3f72ee1957a0d0ad11d8648a6379 upstream. The AMDGPU_GEM_OP_GET_MAPPING_INFO branch of amdgpu_gem_op_ioctl() holds three cleanup-tracked resources before calling kvcalloc(): the drm_gem_object reference from drm_gem_object_lookup(), the drm_exec lock on the looked-up GEM via drm_exec_lock_obj(), and the drm_exec lock on the per-process VM root page directory via amdgpu_vm_lock_pd(). All three are released by the out_exec label that every other error path in this function jumps to. The kvcalloc() failure path returns -ENOMEM directly, skipping out_exec and leaking all three. The leaked per-process VM root PD dma_resv lock is the load-bearing leak: any subsequent operation on the same VM (further GEM ops, command-submission, eviction, TTM shrinker callbacks) blocks on the held lock. DRM_IOCTL_AMDGPU_GEM_OP is DRM_AUTH | DRM_RENDER_ALLOW, so this is an unprivileged-local denial of service against the caller's GPU context, reachable by any process with /dev/dri/renderD* access. Route the failure through out_exec so drm_exec_fini() and drm_gem_object_put() run. Reproduced on stock 7.0.0-10, Ryzen 7 5700U / Radeon Vega (Lucienne): the failing ioctl returns -ENOMEM and a second GET_MAPPING_INFO on the same fd then blocks in drm_exec_lock_obj() on the leaked dma_resv. SIGKILL on the caller does not reap the task; the fd-release path during process exit goes through amdgpu_gem_object_close() -> drm_exec_prepare_obj() on the same lock, leaving the task in D state until the box is rebooted. The patched kernel was not rebuilt and re-tested on this hardware; the fix is mechanical. Tested on a single Lucienne / Vega box only. Ziyi Guo posted an independent INT_MAX-bound check for args->num_entries in the same branch [1]; the two patches are complementary and can land in either order. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Link: https://lore.kernel.org/all/20260208000255.4073363-1-n7l8m4@u.northwestern.edu/ # [1] Signed-off-by: Michael Bommarito Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Alex Deucher (cherry picked from commit b69d3256d79de15f54c322986ff4da68f1d65b0a) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 1631a0431ea8..88938bdb5b70 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1075,8 +1075,10 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * be retried. */ vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); - if (!vm_entries) - return -ENOMEM; + if (!vm_entries) { + r = -ENOMEM; + goto out_exec; + } amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) { if (num_mappings < args->num_entries) { From fa372f4e8aeff6d0d3dd2f14b9165b4013e72a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 18 Feb 2026 12:31:29 +0100 Subject: [PATCH 647/697] drm/amdgpu: fix calling VM invalidation in amdgpu_hmm_invalidate_gfx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 1c824497d8acd3187d585d6187cedc1897dcc871 upstream. Otherwise we don't invalidate page tables on next CS. Signed-off-by: Christian König Reviewed-by: Vitaly Prosyak Tested-by: Vitaly Prosyak Reviewed-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit b6444d1bcbc34f6f2a31a3aab3059be082f3683e) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 2c6a6b858112..621c57cf24bd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -78,6 +78,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); + amdgpu_vm_bo_invalidate(bo, false); r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 0d7c48927f39..ffbae605caaa 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1610,6 +1610,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, { struct amdgpu_bo_va *bo_va; struct dma_resv *resv; + struct amdgpu_bo *bo; bool clear, unlock; int r; @@ -1629,11 +1630,13 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, while (!list_empty(&vm->invalidated)) { bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va, base.vm_status); - resv = bo_va->base.bo->tbo.base.resv; + bo = bo_va->base.bo; + resv = bo->tbo.base.resv; spin_unlock(&vm->status_lock); /* Try to reserve the BO to avoid clearing its ptes */ - if (!adev->debug_vm && dma_resv_trylock(resv)) { + if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && + dma_resv_trylock(resv)) { clear = false; unlock = true; /* The caller is already holding the reservation lock */ From f059b4c493df3e54fe3ffe4658009c31864275da Mon Sep 17 00:00:00 2001 From: Ziyi Guo Date: Sun, 8 Feb 2026 00:02:55 +0000 Subject: [PATCH 648/697] drm/amdgpu: check num_entries in GEM_OP GET_MAPPING_INFO commit a1ba4594232c87c3b8defd6f89a2e40f8b08395d upstream. kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL) at amdgpu_gem.c:1050 uses the user-supplied num_entries directly without any upper bounds check. Since num_entries is a __u32 and sizeof(drm_amdgpu_gem_vm_entry) is 32 bytes, a large num_entries produces an allocation exceeding INT_MAX, triggering WARNING in __kvmalloc_node_noprof(), causing a kernel WARNING, TAINT_WARN, and panic on CONFIG_PANIC_ON_WARN=y systems. Add a size bounds check before we invoke the kvzalloc() to reject oversized num_entries early with -EINVAL. Fixes: 4d82724f7f2b ("drm/amdgpu: Add mapping info option for GEM_OP ioctl") Signed-off-by: Ziyi Guo Signed-off-by: Alex Deucher (cherry picked from commit 1fe7bf5457f6efd7be60b17e23163ba54341d73d) Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 88938bdb5b70..1bc1233487a0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1074,6 +1074,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { + r = -EINVAL; + goto out_exec; + } + vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL); if (!vm_entries) { r = -ENOMEM; From 24b7be239b0b20beaf56910b53da12a79aea2c58 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:31 +0100 Subject: [PATCH 649/697] serial: dz: Fix bootconsole message clobbering at chip reset commit ca904f4b42355287bc5ce8b7550ebe909cda4c2c upstream. In the DZ interface as implemented by the DC7085 gate array the serial transmitters are double buffered, meaning that at the time a transmitter is ready to accept the next character there is one in the transmit shift register still being sent to the line. Issuing a master clear at this time causes this character to be lost, so wait an extra amount of time sufficient for the transmit shift register to drain at 9600bps, which is the baud rate setting used by the firmware console. Mind the specified 1.4us TRDY recovery time in the course and continue using iob() as the completion barrier, since the platforms involved use a write buffer that can delay and combine writes, and reorder them with respect to reads regardless of the MMIO locations accessed and we still lack a platform-independent handler for that. When called from dz_serial_console_init() this is too early for fsleep() to work and even before lpj has been calculated and therefore the delay is actually not sufficient for the transmitter to drain and is merely a placeholder now. This will be addressed in a follow-up change. Fixes: e6ee512f5a77 ("dz.c: Resource management") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.25+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062259080.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/dz.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index eba91daedef8..eb4629505706 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -542,10 +542,31 @@ static int dz_encode_baud_rate(unsigned int baud) static void dz_reset(struct dz_port *dport) { struct dz_mux *mux = dport->mux; + unsigned short tcr; + int loops = 10000; if (mux->initialised) return; + tcr = dz_in(dport, DZ_TCR); + + /* Do not disturb any ongoing transmissions. */ + if (dz_in(dport, DZ_CSR) & DZ_MSE) { + unsigned short csr, mask; + + mask = tcr; + while ((mask & DZ_LNENB) && loops--) { + csr = dz_in(dport, DZ_CSR); + if (!(csr & DZ_TRDY)) + continue; + mask &= ~(1 << ((csr & DZ_TLINE) >> 8)); + dz_out(dport, DZ_TCR, mask); + iob(); + udelay(2); /* 1.4us TRDY recovery. */ + } + udelay(1200); /* Transmitter drain. */ + } + dz_out(dport, DZ_CSR, DZ_CLR); while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); From 2c5b693d918ced53fc096515ff49c196c32dc7fd Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:35 +0100 Subject: [PATCH 650/697] serial: dz: Fix bootconsole handover lockup commit 7f127b2208e5e2b817243cad41fe4211a6d5a7a3 upstream. Calling dz_reset() in the course of setting up the serial device causes line parameters to be reset and the transmitter disabled. We've been lucky in that no message is usually produced to the kernel log between this call and the later call to uart_set_options() in the course of console setup done by dz_serial_console_init(), or the system would hang as the console output handler in the firmware tried to access a port the transmitter of which has been disabled and line parameters messed up. This will change with the next change to the driver, so fix dz_reset() such that line parameters are set for 9600n8 console operation as with the system firmware and the transmitter re-enabled after reset. This also means dz_pm() serves no purpose anymore, so drop it. Fixes: e6ee512f5a77 ("dz.c: Resource management") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.25+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062302010.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/dz.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index eb4629505706..df0a3b0dee0b 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -571,6 +571,18 @@ static void dz_reset(struct dz_port *dport) while (dz_in(dport, DZ_CSR) & DZ_CLR); iob(); + /* + * Set parameters across all lines such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to produce rubbish. + */ + for (int line = 0; line < DZ_NB_PORT; line++) + dz_out(dport, DZ_LPR, DZ_B9600 | DZ_CS8 | line); + + /* Re-enable transmission for the initial PROM-based console. */ + dz_out(dport, DZ_TCR, tcr); + /* Enable scanning. */ dz_out(dport, DZ_CSR, DZ_MSE); @@ -654,26 +666,6 @@ static void dz_set_termios(struct uart_port *uport, struct ktermios *termios, uart_port_unlock_irqrestore(&dport->port, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void dz_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct dz_port *dport = to_dport(uport); - unsigned long flags; - - uart_port_lock_irqsave(&dport->port, &flags); - if (state < 3) - dz_start_tx(&dport->port); - else - dz_stop_tx(&dport->port); - uart_port_unlock_irqrestore(&dport->port, flags); -} - - static const char *dz_type(struct uart_port *uport) { return "DZ"; @@ -769,7 +761,6 @@ static const struct uart_ops dz_ops = { .startup = dz_startup, .shutdown = dz_shutdown, .set_termios = dz_set_termios, - .pm = dz_pm, .type = dz_type, .release_port = dz_release_port, .request_port = dz_request_port, @@ -894,10 +885,7 @@ static int __init dz_console_setup(struct console *co, char *options) if (ret) return ret; - spin_lock_init(&dport->port.lock); /* For dz_pm(). */ - dz_reset(dport); - dz_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); From 2ff0401ffddaccc85f758c8259912d686d052b31 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:48 +0100 Subject: [PATCH 651/697] serial: dz: Convert to use a platform device commit 5d7a49d60b8fda66da60e240fd7315232fa1754f upstream. Prevent a crash from happening as the first serial port is initialised: Console: switching to colour frame buffer device 160x64 tgafb: SFB+ detected, rev=0x02 fb0: Digital ZLX-E1 frame buffer device at 0x1e000000 DECstation DZ serial driver version 1.04 CPU 0 Unable to handle kernel paging request at virtual address 000000bc, epc == 8048b3a4, ra == 80470a78 Oops[#1]: CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.19.0-dirty #35 NONE $ 0 : 00000000 1000ac00 00000004 804707ac $ 4 : 00000000 80e20850 80e20858 81000030 $ 8 : 00000000 8072c81c 00000008 fefefeff $12 : 6c616972 00000006 80c5917f 69726420 $16 : 80e20800 00000000 808f8968 80e20800 $20 : 00000000 807f5a90 808b0094 808d3bc8 $24 : 00000018 80479030 $28 : 80c2e000 80c2fd70 00000069 80470a78 Hi : 00000004 Lo : 00000000 epc : 8048b3a4 __dev_fwnode+0x0/0xc ra : 80470a78 serial_base_ctrl_add+0xa0/0x168 Status: 1000ac04 IEp Cause : 30000008 (ExcCode 02) BadVA : 000000bc PrId : 00000220 (R3000) Modules linked in: Process swapper/0 (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) Stack : 00400044 00400040 8046f4cc 00000000 808a6148 808a0000 808f8968 8086983c 808e0000 8046fc84 1000ac01 00000028 80e20700 802ba3f8 80e20700 80d34a94 80c1b900 80e20700 80e20700 80e20700 80e20700 80444650 00000000 00000000 00000000 807f5a90 808b0094 80447080 00400040 808e0000 80d34a94 808a6148 80d34a94 00000004 80e20700 00000000 8076974c 80469810 80c2fe3c 1000ac01 ... Call Trace: [<8048b3a4>] __dev_fwnode+0x0/0xc [<80470a78>] serial_base_ctrl_add+0xa0/0x168 [<8046fc84>] serial_core_register_port+0x1c8/0x974 [<808c6af0>] dz_init+0x74/0xc8 [<800470e0>] do_one_initcall+0x44/0x2d4 [<808b111c>] kernel_init_freeable+0x258/0x308 [<8072e434>] kernel_init+0x20/0x114 [<80049cd0>] ret_from_kernel_thread+0x14/0x1c Code: 27bd0018 03e00008 2402ffea <8c8200bc> 03e00008 00000000 27bdffc0 afbe0038 afb30024 ---[ end trace 0000000000000000 ]--- -- where a pointer is dereferenced that has been derived from a null pointer to the port's parent device. Since no device is available with legacy probing and it's not anymore a preferable way to discover devices anyway, switch the driver to using a platform device and use it as the port's parent device. Update resource handling accordingly and only request the actual span of addresses used within the slot, which will have had its resource already requested by generic platform device code. Use platform_driver_probe() not just because the DZ device is fixed with solder on board and not straightforward to remove, but foremost because the associated TTY's major device number is the same as used by the zs driver and the first driver to claim it will prevent the other one from using it. Either one DZ device or some SCC devices will be present in a given system but never both at a time, and therefore we want the major device number to be claimed by the first driver to actually successfully bind to its device and platform_driver_probe() is a way to fulfil that. An unfortunate consequence of the switch to a platform device is we now hand the console over from the bootconsole much later in the bootstrap. The firmware console handler appears good enough though to work so late and in particular with interrupts enabled. Conversely only starting the console port so late lets the reset code fully utilise our delay handlers, so switch from udelay() to fsleep() for transmitter draining so as to avoid busy-waiting for an excessive amount of time. Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 Link: https://patch.msgid.link/alpine.DEB.2.21.2605062326540.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/platform.c | 55 ++++++++++++++++++- drivers/tty/serial/dz.c | 116 +++++++++++++++++++-------------------- 2 files changed, 110 insertions(+), 61 deletions(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index c4fcb8c58e01..fdecc91ee22a 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -10,6 +10,13 @@ #include #include +#include + +#include +#include +#include +#include + static struct resource dec_rtc_resources[] = { { .name = "rtc", @@ -30,11 +37,57 @@ static struct platform_device dec_rtc_device = { .num_resources = ARRAY_SIZE(dec_rtc_resources), }; +static struct resource dec_dz_resources[] = { + { .name = "dz", .flags = IORESOURCE_MEM, }, + { .name = "dz", .flags = IORESOURCE_IRQ, }, +}; + +static struct platform_device dec_dz_device = { + .name = "dz", + .id = PLATFORM_DEVID_NONE, + .resource = dec_dz_resources, + .num_resources = ARRAY_SIZE(dec_dz_resources), +}; + +static struct platform_device *dec_dz_devices[] __initdata = { + &dec_dz_device, +}; + static int __init dec_add_devices(void) { + int ret1, ret2; + int num_dz; + int irq, i; + dec_rtc_resources[0].start = RTC_PORT(0); dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1; - return platform_device_register(&dec_rtc_device); + + i = 0; + irq = dec_interrupt[DEC_IRQ_DZ11]; + if (IS_ENABLED(CONFIG_32BIT) && irq >= 0) { + resource_size_t base; + + switch (mips_machtype) { + case MACH_DS23100: + case MACH_DS5100: + base = dec_kn_slot_base + KN01_DZ11; + break; + default: + base = dec_kn_slot_base + KN02_DZ11; + break; + } + dec_dz_device.resource[0].start = base; + dec_dz_device.resource[0].end = base + dec_kn_slot_size - 1; + dec_dz_device.resource[1].start = irq; + dec_dz_device.resource[1].end = irq; + i++; + } + num_dz = i; + + ret1 = platform_device_register(&dec_rtc_device); + ret2 = IS_ENABLED(CONFIG_32BIT) ? + platform_add_devices(dec_dz_devices, num_dz) : 0; + return ret1 ? ret1 : ret2; } device_initcall(dec_add_devices); diff --git a/drivers/tty/serial/dz.c b/drivers/tty/serial/dz.c index df0a3b0dee0b..67b12d7a647d 100644 --- a/drivers/tty/serial/dz.c +++ b/drivers/tty/serial/dz.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +49,6 @@ #include #include -#include - -#include -#include -#include -#include -#include -#include #include "dz.h" @@ -65,7 +58,9 @@ MODULE_LICENSE("GPL"); static char dz_name[] __initdata = "DECstation DZ serial driver version "; -static char dz_version[] __initdata = "1.04"; +static char dz_version[] __initdata = "1.05"; + +#define DZ_IO_SIZE 0x20 /* IOMEM space size. */ struct dz_port { struct dz_mux *mux; @@ -81,6 +76,7 @@ struct dz_mux { }; static struct dz_mux dz_mux; +static struct uart_driver dz_reg; static inline struct dz_port *to_dport(struct uart_port *uport) { @@ -564,7 +560,7 @@ static void dz_reset(struct dz_port *dport) iob(); udelay(2); /* 1.4us TRDY recovery. */ } - udelay(1200); /* Transmitter drain. */ + fsleep(1200); /* Transmitter drain. */ } dz_out(dport, DZ_CSR, DZ_CLR); @@ -681,14 +677,13 @@ static void dz_release_port(struct uart_port *uport) map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); } static int dz_map_port(struct uart_port *uport) { if (!uport->membase) - uport->membase = ioremap(uport->mapbase, - dec_kn_slot_size); + uport->membase = ioremap(uport->mapbase, DZ_IO_SIZE); if (!uport->membase) { printk(KERN_ERR "dz: Cannot map MMIO\n"); return -ENOMEM; @@ -704,8 +699,7 @@ static int dz_request_port(struct uart_port *uport) map_guard = atomic_add_return(1, &mux->map_guard); if (map_guard == 1) { - if (!request_mem_region(uport->mapbase, dec_kn_slot_size, - "dz")) { + if (!request_mem_region(uport->mapbase, DZ_IO_SIZE, "dz")) { atomic_add(-1, &mux->map_guard); printk(KERN_ERR "dz: Unable to reserve MMIO resource\n"); @@ -716,7 +710,7 @@ static int dz_request_port(struct uart_port *uport) if (ret) { map_guard = atomic_add_return(-1, &mux->map_guard); if (!map_guard) - release_mem_region(uport->mapbase, dec_kn_slot_size); + release_mem_region(uport->mapbase, DZ_IO_SIZE); return ret; } return 0; @@ -768,20 +762,15 @@ static const struct uart_ops dz_ops = { .verify_port = dz_verify_port, }; -static void __init dz_init_ports(void) +static int __init dz_probe(struct platform_device *pdev) { - static int first = 1; - unsigned long base; + struct resource *mem_resource, *irq_resource; int line; - if (!first) - return; - first = 0; - - if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100) - base = dec_kn_slot_base + KN01_DZ11; - else - base = dec_kn_slot_base + KN02_DZ11; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; for (line = 0; line < DZ_NB_PORT; line++) { struct dz_port *dport = &dz_mux.dport[line]; @@ -789,14 +778,33 @@ static void __init dz_init_ports(void) dport->mux = &dz_mux; - uport->irq = dec_interrupt[DEC_IRQ_DZ11]; + uport->dev = &pdev->dev; + uport->irq = irq_resource->start; uport->fifosize = 1; uport->iotype = UPIO_MEM; uport->flags = UPF_BOOT_AUTOCONF; uport->ops = &dz_ops; uport->line = line; - uport->mapbase = base; + uport->mapbase = mem_resource->start; uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_DZ_CONSOLE); + + if (uart_add_one_port(&dz_reg, uport)) + uport->dev = NULL; + } + + return 0; +} + +static void __exit dz_remove(struct platform_device *pdev) +{ + int line; + + for (line = DZ_NB_PORT - 1; line >= 0; line--) { + struct dz_port *dport = &dz_mux.dport[line]; + struct uart_port *uport = &dport->port; + + if (uport->dev) + uart_remove_one_port(&dz_reg, uport); } } @@ -879,21 +887,14 @@ static int __init dz_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = dz_map_port(uport); - if (ret) - return ret; - - dz_reset(dport); + if (!dport->mux) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); - - return uart_set_options(&dport->port, co, baud, parity, bits, flow); + return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver dz_reg; static struct console dz_console = { .name = "ttyS", .write = dz_console_print, @@ -904,18 +905,6 @@ static struct console dz_console = { .data = &dz_reg, }; -static int __init dz_serial_console_init(void) -{ - if (!IOASIC) { - dz_init_ports(); - register_console(&dz_console); - return 0; - } else - return -ENXIO; -} - -console_initcall(dz_serial_console_init); - #define SERIAL_DZ_CONSOLE &dz_console #else #define SERIAL_DZ_CONSOLE NULL @@ -931,25 +920,32 @@ static struct uart_driver dz_reg = { .cons = SERIAL_DZ_CONSOLE, }; +static struct platform_driver dz_driver = { + .remove = __exit_p(dz_remove), + .driver = { .name = "dz" }, +}; + static int __init dz_init(void) { - int ret, i; - - if (IOASIC) - return -ENXIO; + int ret; printk("%s%s\n", dz_name, dz_version); - dz_init_ports(); - ret = uart_register_driver(&dz_reg); if (ret) return ret; + ret = platform_driver_probe(&dz_driver, dz_probe); + if (ret) + uart_unregister_driver(&dz_reg); - for (i = 0; i < DZ_NB_PORT; i++) - uart_add_one_port(&dz_reg, &dz_mux.dport[i].port); + return ret; +} - return 0; +static void __exit dz_exit(void) +{ + platform_driver_unregister(&dz_driver); + uart_unregister_driver(&dz_reg); } module_init(dz_init); +module_exit(dz_exit); From b1ceeaef4fbc5108b4521d30f8ea92b98e440579 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:39 +0100 Subject: [PATCH 652/697] serial: zs: Fix bootconsole handover lockup commit 6c05cf72e13314ce9b770b5951695dc5a2152920 upstream. Calling zs_reset() in the course of setting up the serial device causes line parameters to be reset and the transmitter disabled. We've been lucky in that no message is usually produced to the kernel log between this call and the later call to uart_set_options() in the course of console setup done by zs_serial_console_init(), or the system would hang as the console output handler in the firmware tried to access a port the transmitter of which has been disabled and line parameters messed up. This will change with the next change to the driver, so fix zs_reset() such that line parameters are set for 9600n8 console operation as with the system firmware and the transmitter re-enabled after reset. This also means zs_pm() serves no purpose anymore, so drop it. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.23+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062308040.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 0eaaca0eddc0..5f5f549650ec 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -105,18 +105,24 @@ struct zs_parms { static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +/* + * Set parameters in WR5, WR12, WR13 such as not to interfere + * with the initial PROM-based console. Otherwise any output + * produced before the console handover would cause the system + * firmware to hang (TxENAB) or produce rubbish (Tx8, B9600). + */ static u8 zs_init_regs[ZS_NUM_REGS] __initdata = { 0, /* write 0 */ PAR_SPEC, /* write 1 */ 0, /* write 2 */ 0, /* write 3 */ X16CLK | SB1, /* write 4 */ - 0, /* write 5 */ + Tx8 | TxENAB, /* write 5 */ 0, 0, 0, /* write 6, 7, 8 */ MIE | DLC | NV, /* write 9 */ NRZ, /* write 10 */ TCBR | RCBR, /* write 11 */ - 0, 0, /* BRG time constant, write 12 + 13 */ + 0x16, 0x00, /* BRG time constant, write 12 + 13 */ BRSRC | BRENABL, /* write 14 */ 0, /* write 15 */ }; @@ -956,23 +962,6 @@ static void zs_set_termios(struct uart_port *uport, struct ktermios *termios, spin_unlock_irqrestore(&scc->zlock, flags); } -/* - * Hack alert! - * Required solely so that the initial PROM-based console - * works undisturbed in parallel with this one. - */ -static void zs_pm(struct uart_port *uport, unsigned int state, - unsigned int oldstate) -{ - struct zs_port *zport = to_zport(uport); - - if (state < 3) - zport->regs[5] |= TxENAB; - else - zport->regs[5] &= ~TxENAB; - write_zsreg(zport, R5, zport->regs[5]); -} - static const char *zs_type(struct uart_port *uport) { @@ -1055,7 +1044,6 @@ static const struct uart_ops zs_ops = { .startup = zs_startup, .shutdown = zs_shutdown, .set_termios = zs_set_termios, - .pm = zs_pm, .type = zs_type, .release_port = zs_release_port, .request_port = zs_request_port, @@ -1210,7 +1198,6 @@ static int __init zs_console_setup(struct console *co, char *options) return ret; zs_reset(zport); - zs_pm(uport, 0, -1); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); From 81984447eac41d0807107234652d9ef28e7a07cc Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:43 +0100 Subject: [PATCH 653/697] serial: zs: Switch to using channel reset commit 8572955630f30948837088aa98bcbe0532d1ceac upstream. Switch the driver to using the channel reset rather than hardware reset, simplifying handling by removing an interference between channels that causes the other channel to become uninitialised afterwards. There is little difference between the two kinds of reset in terms of register settings that result, and we initialise the whole register set right away anyway. However this prevents a hang from happening should the console output handler in the firmware try to access the other port whose transmitter has been disabled and line parameters messed up. For example this will happen if the keyboard port (port A) is chosen for the system console, unusually but not insanely for a headless system, as the port is wired to a standard DA-15 connector and an adapter can be easily made. Or with the next change in place this would happen for the regular console port (port B), since the keyboard port (port A) will be initialised first. Just remove the unnecessary complication then, a channel reset is good enough. We still need the initialisation marker, now per channel rather than per SCC, as for the console port zs_reset() will be called twice: once early on via zs_serial_console_init() for the console setup only, and then again via zs_config_port() as the port is associated with a TTY device. Fixes: 8b4a40809e53 ("zs: move to the serial subsystem") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # v2.6.23+ Link: https://patch.msgid.link/alpine.DEB.2.21.2605062323430.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/zs.c | 7 ++++--- drivers/tty/serial/zs.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 5f5f549650ec..3fc2554405e9 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -832,21 +832,22 @@ static void zs_shutdown(struct uart_port *uport) static void zs_reset(struct zs_port *zport) { + struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A]; struct zs_scc *scc = zport->scc; int irq; unsigned long flags; spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!scc->initialised) { + if (!zport->initialised) { /* Reset the pointer first, just in case... */ read_zsreg(zport, R0); /* And let the current transmission finish. */ zs_line_drain(zport, irq); - write_zsreg(zport, R9, FHWRES); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); udelay(10); write_zsreg(zport, R9, 0); - scc->initialised = 1; + zport->initialised = 1; } load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 26ef8eafa1c1..8e51f847bc03 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -22,6 +22,7 @@ struct zs_port { struct zs_scc *scc; /* Containing SCC. */ struct uart_port port; /* Underlying UART. */ + int initialised; /* For the console port. */ int clk_mode; /* May be 1, 16, 32, or 64. */ @@ -41,7 +42,6 @@ struct zs_scc { struct zs_port zport[2]; spinlock_t zlock; atomic_t irq_guard; - int initialised; }; #endif /* __KERNEL__ */ From 237dc8c08de3cb293b6607aaee8b13b3a671e267 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 6 May 2026 23:42:52 +0100 Subject: [PATCH 654/697] serial: zs: Convert to use a platform device commit 7cac59d08a73cb866ec51a483a6f3fe0f531947c upstream. Prevent a crash from happening as the first serial port is initialised: Console: switching to mono frame buffer device 160x64 fb0: PMAG-AA frame buffer device at tc0 DECstation Z85C30 serial driver version 0.10 CPU 0 Unable to handle kernel paging request at virtual address 0000002c, epc == 803ab00c, ra == 803aafe0 Oops[#1]: CPU: 0 PID: 1 Comm: swapper Not tainted 6.4.0-rc3-00031-g84a9582fd203-dirty #57 $ 0 : 00000000 10012c00 803aaeb0 00000000 $ 4 : 80e12f60 80e12f50 80e12f58 81000030 $ 8 : 00000000 805ff37c 00000000 33433538 $12 : 65732030 00000006 80c2915d 6c616972 $16 : 80e12f00 807b7630 00000000 00000000 $20 : 00000004 00000348 000001a0 807623b8 $24 : 00000018 00000000 $28 : 80c24000 80c25d60 8078b148 803aafe0 Hi : 00000000 Lo : 00000000 epc : 803ab00c serial_base_ctrl_add+0x78/0xf4 ra : 803aafe0 serial_base_ctrl_add+0x4c/0xf4 Status: 10012c03 KERNEL EXL IE Cause : 00000008 (ExcCode 02) BadVA : 0000002c PrId : 00000440 (R4400SC) Modules linked in: Process swapper (pid: 1, threadinfo=(ptrval), task=(ptrval), tls=00000000) Stack : 80760000 00000cc0 00400044 00400040 803aa02c 80d61ab8 00000000 807b7630 80760000 807623b8 807b7628 803aa644 80386998 00000000 80e17780 80220f68 80e17780 80d61ab8 80c17d80 80e17780 80e17780 8063c798 80e17780 80383fa0 00000010 80e17780 00000000 80386998 807a0000 00000000 00400040 8038f848 807623b8 80d61ab8 00000004 80e17780 00000000 803a68e4 80c25e2c 803bb884 ... Call Trace: [<803ab00c>] serial_base_ctrl_add+0x78/0xf4 [<803aa644>] serial_core_register_port+0x174/0x69c [<8077e9ac>] zs_init+0xc8/0xfc [<800404d4>] do_one_initcall+0x40/0x2ac [<8076cecc>] kernel_init_freeable+0x1e4/0x270 [<80605bec>] kernel_init+0x20/0x108 [<800431e8>] ret_from_kernel_thread+0x14/0x1c Code: 2442aeb0 ae120024 ae0200d0 <8c67002c> 50e00001 8c670000 3c06806e 3c05806e afb30010 ---[ end trace 0000000000000000 ]--- (report at the offending commit) -- where a pointer is dereferenced that has been derived from a null pointer to the port's parent device. Since no device is available with legacy probing and it's not anymore a preferable way to discover devices anyway, switch the driver to using a platform device and use it as the port's parent device. Update resource handling accordingly and only request the actual span of addresses used within the slot, which will have had its resource already requested by generic platform device code. Use platform_driver_probe() not just because SCC devices are fixed with solder on board and not straightforward to remove, but foremost because the associated TTY's major device number is the same as used by the dz driver and the first driver to claim it will prevent the other one from using it. Either one DZ device or some SCC devices will be present in a given system but never both at a time, and therefore we want the major device number to be claimed by the first driver to actually successfully bind to its device and platform_driver_probe() is a way to fulfil that. An unfortunate consequence of the switch to a platform device is we now hand the console over from the bootconsole much later in the bootstrap. The firmware console handler appears good enough though to work so late and in particular with interrupts enabled. Since there is one way only remaining to reach zs_reset() now, remove the port initialisation marker as no longer needed and go through the channel reset unconditionally. Fixes: 84a9582fd203 ("serial: core: Start managing serial controllers to enable runtime PM") Signed-off-by: Maciej W. Rozycki Cc: stable@vger.kernel.org # needs to use .remove_new for <= 6.10 Link: https://patch.msgid.link/alpine.DEB.2.21.2605062328480.46195@angie.orcam.me.uk Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- arch/mips/dec/platform.c | 60 +++++++++++- drivers/tty/serial/zs.c | 192 +++++++++++++++------------------------ drivers/tty/serial/zs.h | 1 - 3 files changed, 129 insertions(+), 124 deletions(-) diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c index fdecc91ee22a..723ce16cbfc0 100644 --- a/arch/mips/dec/platform.c +++ b/arch/mips/dec/platform.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -53,10 +54,37 @@ static struct platform_device *dec_dz_devices[] __initdata = { &dec_dz_device, }; +static struct resource dec_zs_resources[][2] = { + { + { .name = "scc0", .flags = IORESOURCE_MEM, }, + { .name = "scc0", .flags = IORESOURCE_IRQ, }, + }, + { + { .name = "scc1", .flags = IORESOURCE_MEM, }, + { .name = "scc1", .flags = IORESOURCE_IRQ, }, + }, +}; + +static struct platform_device dec_zs_device[] = { + { + .name = "zs", + .id = 0, + .resource = dec_zs_resources[0], + .num_resources = ARRAY_SIZE(dec_zs_resources[0]), + }, + { + .name = "zs", + .id = 1, + .resource = dec_zs_resources[1], + .num_resources = ARRAY_SIZE(dec_zs_resources[1]), + }, +}; + static int __init dec_add_devices(void) { - int ret1, ret2; - int num_dz; + struct platform_device *dec_zs_devices[ARRAY_SIZE(dec_zs_device)]; + int ret1, ret2, ret3; + int num_dz, num_zs; int irq, i; dec_rtc_resources[0].start = RTC_PORT(0); @@ -84,10 +112,36 @@ static int __init dec_add_devices(void) } num_dz = i; + i = 0; + irq = dec_interrupt[DEC_IRQ_SCC0]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC0; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + irq = dec_interrupt[DEC_IRQ_SCC1]; + if (irq >= 0) { + resource_size_t base = dec_kn_slot_base + IOASIC_SCC1; + + dec_zs_device[i].resource[0].start = base; + dec_zs_device[i].resource[0].end = base + dec_kn_slot_size - 1; + dec_zs_device[i].resource[1].start = irq; + dec_zs_device[i].resource[1].end = irq; + dec_zs_devices[i] = &dec_zs_device[i]; + i++; + } + num_zs = i; + ret1 = platform_device_register(&dec_rtc_device); ret2 = IS_ENABLED(CONFIG_32BIT) ? platform_add_devices(dec_dz_devices, num_dz) : 0; - return ret1 ? ret1 : ret2; + ret3 = platform_add_devices(dec_zs_devices, num_zs); + return ret1 ? ret1 : ret2 ? ret2 : ret3; } device_initcall(dec_add_devices); diff --git a/drivers/tty/serial/zs.c b/drivers/tty/serial/zs.c index 3fc2554405e9..8cafb79912cf 100644 --- a/drivers/tty/serial/zs.c +++ b/drivers/tty/serial/zs.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -66,10 +67,6 @@ #include -#include -#include -#include - #include "zs.h" @@ -79,7 +76,7 @@ MODULE_LICENSE("GPL"); static char zs_name[] __initdata = "DECstation Z85C30 serial driver version "; -static char zs_version[] __initdata = "0.10"; +static char zs_version[] __initdata = "0.11"; /* * It would be nice to dynamically allocate everything that @@ -98,12 +95,8 @@ static char zs_version[] __initdata = "0.10"; #define to_zport(uport) container_of(uport, struct zs_port, port) -struct zs_parms { - resource_size_t scc[ZS_NUM_SCCS]; - int irq[ZS_NUM_SCCS]; -}; - static struct zs_scc zs_sccs[ZS_NUM_SCCS]; +static struct uart_driver zs_reg; /* * Set parameters in WR5, WR12, WR13 such as not to interfere @@ -839,16 +832,15 @@ static void zs_reset(struct zs_port *zport) spin_lock_irqsave(&scc->zlock, flags); irq = !irqs_disabled_flags(flags); - if (!zport->initialised) { - /* Reset the pointer first, just in case... */ - read_zsreg(zport, R0); - /* And let the current transmission finish. */ - zs_line_drain(zport, irq); - write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); - udelay(10); - write_zsreg(zport, R9, 0); - zport->initialised = 1; - } + + /* Reset the pointer first, just in case... */ + read_zsreg(zport, R0); + /* And let the current transmission finish. */ + zs_line_drain(zport, irq); + write_zsreg(zport, R9, zport == zport_a ? CHRA : CHRB); + udelay(10); + write_zsreg(zport, R9, 0); + load_zsregs(zport, zport->regs, irq); spin_unlock_irqrestore(&scc->zlock, flags); } @@ -1055,63 +1047,62 @@ static const struct uart_ops zs_ops = { /* * Initialize Z85C30 port structures. */ -static int __init zs_probe_sccs(void) +static int __init zs_probe(struct platform_device *pdev) { - static int probed; - struct zs_parms zs_parms; - int chip, side, irq; - int n_chips = 0; + struct resource *mem_resource, *irq_resource; + int chip, side; int i; - if (probed) - return 0; + mem_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq_resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mem_resource || !irq_resource) + return -ENODEV; - irq = dec_interrupt[DEC_IRQ_SCC0]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC0; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0]; - n_chips++; - } - irq = dec_interrupt[DEC_IRQ_SCC1]; - if (irq >= 0) { - zs_parms.scc[n_chips] = IOASIC_SCC1; - zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1]; - n_chips++; - } - if (!n_chips) - return -ENXIO; - - probed = 1; - - for (chip = 0; chip < n_chips; chip++) { - spin_lock_init(&zs_sccs[chip].zlock); - for (side = 0; side < ZS_NUM_CHAN; side++) { - struct zs_port *zport = &zs_sccs[chip].zport[side]; - struct uart_port *uport = &zport->port; - - zport->scc = &zs_sccs[chip]; - zport->clk_mode = 16; - - uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); - uport->irq = zs_parms.irq[chip]; - uport->uartclk = ZS_CLOCK; - uport->fifosize = 1; - uport->iotype = UPIO_MEM; - uport->flags = UPF_BOOT_AUTOCONF; - uport->ops = &zs_ops; - uport->line = chip * ZS_NUM_CHAN + side; - uport->mapbase = dec_kn_slot_base + - zs_parms.scc[chip] + - (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; - - for (i = 0; i < ZS_NUM_REGS; i++) - zport->regs[i] = zs_init_regs[i]; - } + chip = pdev->id; + spin_lock_init(&zs_sccs[chip].zlock); + for (side = 0; side < ZS_NUM_CHAN; side++) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + zport->scc = &zs_sccs[chip]; + zport->clk_mode = 16; + + uport->dev = &pdev->dev; + uport->has_sysrq = IS_ENABLED(CONFIG_SERIAL_ZS_CONSOLE); + uport->irq = irq_resource->start; + uport->uartclk = ZS_CLOCK; + uport->fifosize = 1; + uport->iotype = UPIO_MEM; + uport->flags = UPF_BOOT_AUTOCONF; + uport->ops = &zs_ops; + uport->line = chip * ZS_NUM_CHAN + side; + uport->mapbase = mem_resource->start + + (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE; + + for (i = 0; i < ZS_NUM_REGS; i++) + zport->regs[i] = zs_init_regs[i]; + + if (uart_add_one_port(&zs_reg, uport)) + uport->dev = NULL; } return 0; } +static void __exit zs_remove(struct platform_device *pdev) +{ + int chip, side; + + chip = pdev->id; + for (side = ZS_NUM_CHAN - 1; side >= 0; side--) { + struct zs_port *zport = &zs_sccs[chip].zport[side]; + struct uart_port *uport = &zport->port; + + if (uport->dev) + uart_remove_one_port(&zs_reg, uport); + } +} + #ifdef CONFIG_SERIAL_ZS_CONSOLE static void zs_console_putchar(struct uart_port *uport, unsigned char ch) @@ -1192,20 +1183,14 @@ static int __init zs_console_setup(struct console *co, char *options) int bits = 8; int parity = 'n'; int flow = 'n'; - int ret; - - ret = zs_map_port(uport); - if (ret) - return ret; - - zs_reset(zport); + if (!zport->scc) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(uport, co, baud, parity, bits, flow); } -static struct uart_driver zs_reg; static struct console zs_console = { .name = "ttyS", .write = zs_console_write, @@ -1216,23 +1201,6 @@ static struct console zs_console = { .data = &zs_reg, }; -/* - * Register console. - */ -static int __init zs_serial_console_init(void) -{ - int ret; - - ret = zs_probe_sccs(); - if (ret) - return ret; - register_console(&zs_console); - - return 0; -} - -console_initcall(zs_serial_console_init); - #define SERIAL_ZS_CONSOLE &zs_console #else #define SERIAL_ZS_CONSOLE NULL @@ -1248,47 +1216,31 @@ static struct uart_driver zs_reg = { .cons = SERIAL_ZS_CONSOLE, }; +static struct platform_driver zs_driver = { + .remove = __exit_p(zs_remove), + .driver = { .name = "zs" }, +}; + /* zs_init inits the driver. */ static int __init zs_init(void) { - int i, ret; + int ret; pr_info("%s%s\n", zs_name, zs_version); - /* Find out how many Z85C30 SCCs we have. */ - ret = zs_probe_sccs(); - if (ret) - return ret; - ret = uart_register_driver(&zs_reg); if (ret) return ret; + ret = platform_driver_probe(&zs_driver, zs_probe); + if (ret) + uart_unregister_driver(&zs_reg); - for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_add_one_port(&zs_reg, uport); - } - - return 0; + return ret; } static void __exit zs_exit(void) { - int i; - - for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) { - struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN]; - struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN]; - struct uart_port *uport = &zport->port; - - if (zport->scc) - uart_remove_one_port(&zs_reg, uport); - } - + platform_driver_unregister(&zs_driver); uart_unregister_driver(&zs_reg); } diff --git a/drivers/tty/serial/zs.h b/drivers/tty/serial/zs.h index 8e51f847bc03..e0d3c189b33f 100644 --- a/drivers/tty/serial/zs.h +++ b/drivers/tty/serial/zs.h @@ -22,7 +22,6 @@ struct zs_port { struct zs_scc *scc; /* Containing SCC. */ struct uart_port port; /* Underlying UART. */ - int initialised; /* For the console port. */ int clk_mode; /* May be 1, 16, 32, or 64. */ From 5f2172d799f3d47b3e51b50bbe20acaaa4d36e6a Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:23 +0200 Subject: [PATCH 655/697] serial: core: introduce guard(uart_port_lock_check_sysrq_irqsave) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c3cce2e67bb22a223f5b8ef05db0fcde70994068 upstream. uart_handle_break() and uart_prepare_sysrq_char() (in include/linux/serial_core.h) capture a SysRq character into port->sysrq_ch while the port lock is held and rely on the unlock helper -- uart_unlock_and_check_sysrq_irqrestore() -- to dispatch the captured character to handle_sysrq() on scope exit. The existing guard(uart_port_lock_irqsave) cannot be used by IRQ handlers that process RX, because its destructor calls plain uart_port_unlock_irqrestore() and silently drops port->sysrq_ch. Add a dedicated guard(uart_port_lock_check_sysrq_irqsave) variant whose destructor is the sysrq-aware unlock helper. The lock side is identical to uart_port_lock_irqsave -- only the unlock-time behaviour differs. Callers that may capture SysRq characters must use guard(uart_port_lock_check_sysrq_irqsave); the existing guard(uart_port_lock_irqsave) keeps its current plain-unlock semantics for the many callers that do not process RX. The new macro is placed after the CONFIG_MAGIC_SYSRQ_SERIAL block so both definitions of uart_unlock_and_check_sysrq_irqrestore() (sysrq enabled and disabled) are visible at expansion time. When CONFIG_MAGIC_SYSRQ_SERIAL=n the destructor degenerates to plain uart_port_unlock_irqrestore(), so there is no overhead. No functional change on its own; users are converted in the following patches. Cc: stable@vger.kernel.org Signed-off-by: Jacques Nilo Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/3849af4bc55d5d2a424fa850844e94d641b2f8a6.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman --- include/linux/serial_core.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 666430b47899..110ad4e2aef9 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -1274,6 +1274,18 @@ static inline void uart_unlock_and_check_sysrq_irqrestore(struct uart_port *port } #endif /* CONFIG_MAGIC_SYSRQ_SERIAL */ +/* + * Variant of guard(uart_port_lock_irqsave) for IRQ handlers that may capture + * a SysRq character via uart_prepare_sysrq_char(). The destructor uses the + * sysrq-aware unlock helper so that a captured port->sysrq_ch is dispatched + * to handle_sysrq() on scope exit. The plain guard variant silently drops + * sysrq_ch and must not be used by callers that process RX. + */ +DEFINE_LOCK_GUARD_1(uart_port_lock_check_sysrq_irqsave, struct uart_port, + uart_port_lock_irqsave(_T->lock, &_T->flags), + uart_unlock_and_check_sysrq_irqrestore(_T->lock, _T->flags), + unsigned long flags); + /* * We do the SysRQ and SAK checking like this... */ From 7f8b194ed7206bc866d554b9f63f47e11d97c2c0 Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:24 +0200 Subject: [PATCH 656/697] serial: 8250: dispatch SysRq character in serial8250_handle_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 71f42b2149a1307a97165b409493665579462ea0 upstream. serial8250_handle_irq() captures a SysRq character into port->sysrq_ch inside serial8250_handle_irq_locked() via uart_prepare_sysrq_char() (reached from serial8250_read_char()). Dispatch of that captured character to handle_sysrq() is expected to happen at port-unlock time, through uart_unlock_and_check_sysrq[_irqrestore](). After commit 8324a54f604d ("serial: 8250: Add serial8250_handle_irq_locked()") the function was reduced to a wrapper that takes the port lock via guard(uart_port_lock_irqsave) whose destructor is plain uart_port_unlock_irqrestore(). The sysrq-aware unlock helper is no longer called, so port->sysrq_ch is captured but never dispatched: BREAK + SysRq key is consumed silently. This was the very condition Johan Hovold's 853a9ae29e978 ("serial: 8250: fix handle_irq locking", 2021) introduced uart_unlock_and_check_sysrq_irqrestore() to address. Switch to the new guard(uart_port_lock_check_sysrq_irqsave), whose destructor is the sysrq-aware unlock helper, restoring the pre-split behaviour. Update the Context: comment on serial8250_handle_irq_locked() so future HW-specific 8250 wrappers know to use the same guard or the explicit sysrq-aware unlock. Verified on RTL8196E with CONFIG_MAGIC_SYSRQ_SERIAL=y: BREAK + 'h' on the console UART produces the SysRq help dump in dmesg and the brk counter in /proc/tty/driver/serial increments correctly. Fixes: 8324a54f604d ("serial: 8250: Add serial8250_handle_irq_locked()") Cc: stable@vger.kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Jacques Nilo Link: https://patch.msgid.link/52692ae6c3501f7940347cef364ad7fcacaab7e5.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8785961a2a82..0c633639f765 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1784,7 +1784,10 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) } /* - * Context: port's lock must be held by the caller. + * Context: port's lock must be held by the caller. The caller must + * release it via guard(uart_port_lock_check_sysrq_irqsave) or + * uart_unlock_and_check_sysrq_irqrestore(), which captures SysRq + * character on unlock. */ void serial8250_handle_irq_locked(struct uart_port *port, unsigned int iir) { @@ -1837,7 +1840,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if (iir & UART_IIR_NO_INT) return 0; - guard(uart_port_lock_irqsave)(port); + guard(uart_port_lock_check_sysrq_irqsave)(port); serial8250_handle_irq_locked(port, iir); return 1; From 71b88acec0a7142964b1a2b77faa5c51072244a2 Mon Sep 17 00:00:00 2001 From: Jacques Nilo Date: Wed, 13 May 2026 15:30:25 +0200 Subject: [PATCH 657/697] serial: 8250_dw: dispatch SysRq character in dw8250_handle_irq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 2e211723953f7740e54b53f3d3a0d5e351a5e223 upstream. dw8250_handle_irq() calls serial8250_handle_irq_locked() with the port lock held via guard(uart_port_lock_irqsave). The guard destructor is plain uart_port_unlock_irqrestore(), so a SysRq character captured into port->sysrq_ch by uart_prepare_sysrq_char() is dropped without ever being dispatched to handle_sysrq(). This is the same regression pattern as in serial8250_handle_irq(), introduced when 883c5a2bc934 ("serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling") moved the function to the guard()-based locking scheme without using the sysrq-aware unlock helper. Switch to guard(uart_port_lock_check_sysrq_irqsave) so that captured sysrq_ch is dispatched on scope exit, matching the fix in serial8250_handle_irq(). Fixes: 883c5a2bc934 ("serial: 8250_dw: Rework dw8250_handle_irq() locking and IIR handling") Cc: stable@vger.kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Jacques Nilo Link: https://patch.msgid.link/ed56fcaf4af24e4ed011a7bce206e0182acb761c.1778675349.git.jnilo@free.fr Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index b1f0303aceab..4103178725e3 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -427,7 +427,7 @@ static int dw8250_handle_irq(struct uart_port *p) unsigned int quirks = d->pdata->quirks; unsigned int status; - guard(uart_port_lock_irqsave)(p); + guard(uart_port_lock_check_sysrq_irqsave)(p); switch (FIELD_GET(DW_UART_IIR_IID, iir)) { case UART_IIR_NO_INT: From 4b0e87f9b50ff5136228b3c30dee6b399c9b473f Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 12:59:24 -0400 Subject: [PATCH 658/697] platform/x86/intel/vsec: Refactor base_addr handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 904b333fc51cc045941df9656302449a0fc9978e ] The base_addr field in intel_vsec_platform_info was originally added to support devices that emulate PCI VSEC capabilities in MMIO. Previously, the code would check at registration time whether base_addr was set, falling back to the PCI BAR if not. Refactor this by making base_addr an explicit function parameter. This clarifies ownership of the value and removes conditional logic from intel_vsec_add_dev(). It also enables making intel_vsec_platform_info const in a later patch, since the function no longer needs to write to info->base_addr. No functional change intended. Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-2-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 348ccc754d89 ("platform/x86/intel/vsec: Fix enable_cnt imbalance on PCIe error recovery") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/vsec.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index f66f0ce8559b..1f041dc1a3f8 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -271,14 +271,13 @@ EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, struct intel_vsec_platform_info *info, - unsigned long cap_id) + unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; struct resource __free(kfree) *res = NULL; struct resource *tmp; struct device *parent; unsigned long quirks = info->quirks; - u64 base_addr; int i; if (info->parent) @@ -310,11 +309,6 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; - if (info->base_addr) - base_addr = info->base_addr; - else - base_addr = pdev->resource[header->tbir].start; - /* * The DVSEC/VSEC contains the starting offset and count for a block of * discovery tables. Create a resource array of these tables to the @@ -412,7 +406,8 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) static int intel_vsec_register_device(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info) + struct intel_vsec_platform_info *info, + u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; struct vsec_priv *priv; @@ -428,7 +423,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, * For others using the exported APIs, add the device directly. */ if (!pci_match_id(intel_vsec_pci_ids, pdev)) - return intel_vsec_add_dev(pdev, header, info, cap_id); + return intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); priv = pci_get_drvdata(pdev); if (priv->state[cap_id] == STATE_REGISTERED || @@ -444,7 +439,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, consumer_deps = get_consumer_dependencies(priv, cap_id); if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { - ret = intel_vsec_add_dev(pdev, header, info, cap_id); + ret = intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); if (ret) priv->state[cap_id] = STATE_SKIP; else @@ -464,7 +459,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(pdev, *header, info); + ret = intel_vsec_register_device(pdev, *header, info, info->base_addr); if (!ret) have_devices = true; } @@ -512,7 +507,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -557,7 +553,8 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; From 1730c91a8b9a319e272558a40be08b4d936f4078 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 12:59:25 -0400 Subject: [PATCH 659/697] platform/x86/intel/vsec: Make driver_data info const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 9577c74c96f88d807d1ba005adbf5952e7127e55 ] Treat PCI id->driver_data (intel_vsec_platform_info) as read-only by making vsec_priv->info a const pointer and updating all function signatures to accept const intel_vsec_platform_info *. This improves const-correctness and clarifies that the platform info data from the driver_data table is not meant to be modified at runtime. No functional changes intended. Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-3-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Stable-dep-of: 348ccc754d89 ("platform/x86/intel/vsec: Fix enable_cnt imbalance on PCIe error recovery") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/vsec.c | 20 ++++++++++---------- include/linux/intel_vsec.h | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 1f041dc1a3f8..008a840a6ac8 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -42,7 +42,7 @@ enum vsec_device_state { }; struct vsec_priv { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct device *suppliers[VSEC_FEATURE_COUNT]; struct oobmsm_plat_info plat_info; enum vsec_device_state state[VSEC_FEATURE_COUNT]; @@ -270,7 +270,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info, + const struct intel_vsec_platform_info *info, unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; @@ -406,7 +406,7 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) static int intel_vsec_register_device(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info, + const struct intel_vsec_platform_info *info, u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; @@ -452,7 +452,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, } static bool intel_vsec_walk_header(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; bool have_devices = false; @@ -468,7 +468,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, } static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -519,7 +519,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, } static bool intel_vsec_walk_vsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -565,7 +565,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, } int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { if (!pdev || !info || !info->headers) return -EINVAL; @@ -578,7 +578,7 @@ int intel_vsec_register(struct pci_dev *pdev, EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); static bool intel_vsec_get_features(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool found = false; @@ -622,7 +622,7 @@ static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct vsec_priv *priv; int num_caps, ret; int run_once = 0; @@ -633,7 +633,7 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return ret; pci_save_state(pdev); - info = (struct intel_vsec_platform_info *)id->driver_data; + info = (const struct intel_vsec_platform_info *)id->driver_data; if (!info) return -EINVAL; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 53f6fe88e369..d7f1f531660a 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -199,13 +199,13 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device #if IS_ENABLED(CONFIG_INTEL_VSEC) int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info); + const struct intel_vsec_platform_info *info); int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, struct intel_vsec_device *vsec_dev); struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); #else static inline int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { return -ENODEV; } From 81181a39bde9ad5b44a1b2d68702da6a8c84a565 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 29 May 2026 12:59:26 -0400 Subject: [PATCH 660/697] platform/x86/intel/vsec: Fix enable_cnt imbalance on PCIe error recovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 348ccc754d8939e21ca5956ff45720b81d6e407f ] After a PCIe Uncorrectable Error has been reported by a device with Intel Vendor Specific Extended Capabilities and has been recovered through a Secondary Bus Reset, its driver calls intel_vsec_pci_probe() to rescan and reinitialize VSECs. intel_vsec_pci_probe() invokes pcim_enable_device() and thereby adds another devm action which calls pcim_disable_device() on driver unbind. So once the driver unbinds, pcim_disable_device() will be called as many times as an Uncorrectable Error occurred, plus one. This will lead to an enable_cnt imbalance on driver unbind. Additionally, since commit dc957ab6aa05 ("platform/x86/intel/vsec: Add private data for per-device data"), a devm_kzalloc() allocation is leaked on every Uncorrectable Error. Avoid by splitting the VSEC rescan out of intel_vsec_pci_probe() into a separate helper and calling that on PCIe error recovery. Fixes: 936874b77dd0 ("platform/x86/intel/vsec: Add PCI error recovery support to Intel PMT") Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v6.0+ Link: https://patch.msgid.link/bd594d09fa866dc51dddc9a447c3b23f9b1402cc.1778736835.git.lukas@wunner.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/intel/vsec.c | 54 +++++++++++++++++-------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 008a840a6ac8..96fc27d88fbf 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -620,29 +620,13 @@ static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) } } -static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +static int intel_vsec_pci_init(struct pci_dev *pdev) { - const struct intel_vsec_platform_info *info; - struct vsec_priv *priv; - int num_caps, ret; + struct vsec_priv *priv = pci_get_drvdata(pdev); + const struct intel_vsec_platform_info *info = priv->info; int run_once = 0; bool found_any = false; - - ret = pcim_enable_device(pdev); - if (ret) - return ret; - - pci_save_state(pdev); - info = (const struct intel_vsec_platform_info *)id->driver_data; - if (!info) - return -EINVAL; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->info = info; - pci_set_drvdata(pdev, priv); + int num_caps; num_caps = hweight_long(info->caps); while (num_caps--) { @@ -663,6 +647,31 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return 0; } +static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + const struct intel_vsec_platform_info *info; + struct vsec_priv *priv; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_save_state(pdev); + info = (const struct intel_vsec_platform_info *)id->driver_data; + if (!info) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->info = info; + pci_set_drvdata(pdev, priv); + + return intel_vsec_pci_init(pdev); +} + int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, struct intel_vsec_device *vsec_dev) { @@ -796,7 +805,6 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) { struct intel_vsec_device *intel_vsec_dev; pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; - const struct pci_device_id *pci_dev_id; unsigned long index; dev_info(&pdev->dev, "Resetting PCI slot\n"); @@ -817,10 +825,8 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) devm_release_action(&pdev->dev, intel_vsec_remove_aux, &intel_vsec_dev->auxdev); } - pci_disable_device(pdev); pci_restore_state(pdev); - pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); - intel_vsec_pci_probe(pdev, pci_dev_id); + intel_vsec_pci_init(pdev); out: return status; From fed725cace3ab1c4f7f8182e35029a603d953187 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Fri, 29 May 2026 14:07:46 -0400 Subject: [PATCH 661/697] x86/mm: Disable broadcast TLB flush when PCID is disabled [ Upstream commit 44126343d58c68adaa8343fbf1c07dd20078c35e ] Booting with "nopcid" clears X86_FEATURE_PCID and keeps CR4.PCIDE from being set to one. On AMD CPUs that support INVLPGB, broadcast TLB flushing remains enabled. There are two checks that decide whether the global ASID code runs, mm_global_asid() and consider_global_asid(), that key off of the X86_FEATURE_INVLPGB feature. Once an mm becomes active on more than three CPUs, consider_global_asid() assigns it a global ASID, after which flush_tlb_mm_range() takes the broadcast_tlb_flush() path using a non-zero PCID. Issuing an INVLPGB with a non-zero PCID while CR4.PCIDE is not set results in a #GP: Oops: general protection fault, kernel NULL pointer dereference 0x1: 0000 [#1] SMP NOPTI CPU: 158 UID: 0 PID: 3119 Comm: snap Not tainted 7.1.0-rc3 #1 PREEMPT(full) Hardware name: ... RIP: 0010:broadcast_tlb_flush Code: ... 89 da 48 83 c8 07 <0f> 01 fe eb 08 cc cc cc ... Call Trace: flush_tlb_mm_range ptep_clear_flush wp_page_copy ? _raw_spin_unlock __handle_mm_fault handle_mm_fault do_user_addr_fault exc_page_fault asm_exc_page_fault All processors that support broadcast TLB invalidation also have PCID support, so it is only the "nopcid" scenario that is of concern. In this situation just disable the broadcast TLB support using the CPUID dependency support by making X86_FEATURE_INVLPGB dependent on X86_FEATURE_PCID. [ bp: Massage commit message. ] Fixes: 4afeb0ed1753 ("x86/mm: Enable broadcast TLB invalidation for multi-threaded processes") Suggested-by: Dave Hansen Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Tom Lendacky Signed-off-by: Borislav Petkov (AMD) Acked-by: Rik van Riel Cc: Link: https://patch.msgid.link/b915acfd63e8b2a094fdeb8dc608738072518764.1779296450.git.thomas.lendacky@amd.com [ adjusted insertion point to after X86_FEATURE_SPEC_CTRL_SSBD ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/cpuid-deps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c index 46efcbd6afa4..155df2e58674 100644 --- a/arch/x86/kernel/cpu/cpuid-deps.c +++ b/arch/x86/kernel/cpu/cpuid-deps.c @@ -89,6 +89,7 @@ static const struct cpuid_dep cpuid_deps[] = { { X86_FEATURE_SHSTK, X86_FEATURE_XSAVES }, { X86_FEATURE_FRED, X86_FEATURE_LKGS }, { X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL }, + { X86_FEATURE_INVLPGB, X86_FEATURE_PCID }, {} }; From 46cb765e2e5ad52303ea157e10d370bb6b7acbbf Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 29 May 2026 14:35:07 -0400 Subject: [PATCH 662/697] rxrpc: Fix DATA decrypt vs splice() by copying data to buffer in recvmsg [ Upstream commit d2bc90cf6c75cb96d2ce549be6c35efa3099d25b ] This improves the fix for CVE-2026-43500. Fix the pagecache corruption from in-place decryption of a DATA packet transmitted locally by splice() by getting rid of the packet sharing in the I/O thread and unconditionally extracting the packet content into a bounce buffer in which the buffer is decrypted. recvmsg() (or the kernel equivalent) then copies the data from the bounce buffer to the destination buffer. The sk_buff then remains unmodified. This has an additional advantage in that the packet is then arranged in the buffer with the correct alignment required for the crypto algorithms to process directly. The performance of the crypto does seem to be a little faster and, surprisingly, the unencrypted performance doesn't seem to change much - possibly due to removing complexity from the I/O thread. Yet another advantage is that the I/O thread doesn't have to copy packets which would slow down packet distribution, ACK generation, etc.. The buffer belongs to the call and is allocated initially at 2K, sufficiently large to hold a whole jumbo subpacket, but the buffer will be increased in size if needed. However, to take this work, MSG_PEEK may cause a later packet to be decrypted into the buffer, in which case the earlier one will need re-decrypting for a subsequent recvmsg(). Note that rx_pkt_offset may legitimately see 0 as a valid offset now, so switch to using USHRT_MAX to indicate an invalid offset. Note also that I would generally prefer to replace the buffers of the current sk_buff with a new kmalloc'd buffer of the right size, ditching the old data and frags as this makes the handling of MSG_PEEK easier and removes the re-decryption issue, but this looks like quite a complicated thing to achieve. skb_morph() looks half way to what I want, but I don't want to have to allocate a new sk_buff. Fixes: d0d5c0cd1e71 ("rxrpc: Use skb_unshare() rather than skb_cow_data()") Reported-by: Hyunwoo Kim Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/ Signed-off-by: David Howells cc: Simon Horman cc: Jiayuan Chen cc: linux-afs@lists.infradead.org Reviewed-by: Jeffrey Altman Tested-by: Marc Dionne Link: https://patch.msgid.link/20260515230516.2718212-3-dhowells@redhat.com Signed-off-by: Jakub Kicinski Stable-dep-of: 8bfab4b6ffc2 ("rxrpc: Fix RESPONSE packet verification to extract skb to a linear buffer") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 7 +++- net/rxrpc/call_event.c | 22 +---------- net/rxrpc/call_object.c | 2 + net/rxrpc/insecure.c | 3 -- net/rxrpc/recvmsg.c | 68 +++++++++++++++++++++++++------- net/rxrpc/rxgk.c | 51 ++++++++++++------------ net/rxrpc/rxgk_common.h | 82 +++++++++++++++++++++++++++++++++++++++ net/rxrpc/rxkad.c | 86 +++++++++++++++-------------------------- 8 files changed, 201 insertions(+), 120 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 27c2aa2dd023..783367eea798 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -213,8 +213,6 @@ struct rxrpc_skb_priv { struct { u16 offset; /* Offset of data */ u16 len; /* Length of data */ - u8 flags; -#define RXRPC_RX_VERIFIED 0x01 }; struct { rxrpc_seq_t first_ack; /* First packet in acks table */ @@ -774,6 +772,11 @@ struct rxrpc_call { struct sk_buff_head recvmsg_queue; /* Queue of packets ready for recvmsg() */ struct sk_buff_head rx_queue; /* Queue of packets for this call to receive */ struct sk_buff_head rx_oos_queue; /* Queue of out of sequence packets */ + void *rx_dec_buffer; /* Decryption buffer */ + unsigned short rx_dec_bsize; /* rx_dec_buffer size */ + unsigned short rx_dec_offset; /* Decrypted packet data offset */ + unsigned short rx_dec_len; /* Decrypted packet data len */ + rxrpc_seq_t rx_dec_seq; /* Packet in decryption buffer */ rxrpc_seq_t rx_highest_seq; /* Higest sequence number received */ rxrpc_seq_t rx_consumed; /* Highest packet consumed */ diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c index 2b19b252225e..fec59d9338b9 100644 --- a/net/rxrpc/call_event.c +++ b/net/rxrpc/call_event.c @@ -332,27 +332,7 @@ bool rxrpc_input_call_event(struct rxrpc_call *call) saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK; - if (sp->hdr.type == RXRPC_PACKET_TYPE_DATA && - sp->hdr.securityIndex != 0 && - (skb_cloned(skb) || - skb_has_frag_list(skb) || - skb_has_shared_frag(skb))) { - /* Unshare the packet so that it can be - * modified by in-place decryption. - */ - struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); - - if (nskb) { - rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); - rxrpc_input_call_packet(call, nskb); - rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx); - } else { - /* OOM - Drop the packet. */ - rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); - } - } else { - rxrpc_input_call_packet(call, skb); - } + rxrpc_input_call_packet(call, skb); rxrpc_free_skb(skb, rxrpc_skb_put_call_rx); did_receive = true; } diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index f035f486c139..fcb9d38bb521 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -152,6 +152,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, spin_lock_init(&call->notify_lock); refcount_set(&call->ref, 1); call->debug_id = debug_id; + call->rx_pkt_offset = USHRT_MAX; call->tx_total_len = -1; call->tx_jumbo_max = 1; call->next_rx_timo = 20 * HZ; @@ -553,6 +554,7 @@ static void rxrpc_cleanup_rx_buffers(struct rxrpc_call *call) rxrpc_purge_queue(&call->recvmsg_queue); rxrpc_purge_queue(&call->rx_queue); rxrpc_purge_queue(&call->rx_oos_queue); + kfree(call->rx_dec_buffer); } /* diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index 0a260df45d25..7a26c6097d03 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -32,9 +32,6 @@ static int none_secure_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb) static int none_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) { - struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - - sp->flags |= RXRPC_RX_VERIFIED; return 0; } diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index e1f7513a46db..c940600117a4 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -147,15 +147,52 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call) } /* - * Decrypt and verify a DATA packet. + * Decrypt and verify a DATA packet. The content of the packet is pulled out + * into a flat buffer rather than decrypting in place in the skbuff. This also + * has the advantage of aligning the buffer correctly for the crypto routines. + * + * We keep track of the sequence number of the packet currently decrypted into + * the buffer in ->rx_dec_seq. If MSG_PEEK is used and steps onto a new + * packet, subsequent recvmsg() calls will have to go back and re-decrypt the + * current packet. */ static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + int ret; - if (sp->flags & RXRPC_RX_VERIFIED) - return 0; - return call->security->verify_packet(call, skb); + if (sp->len > call->rx_dec_bsize) { + /* Make sure we can hold a 1412-byte jumbo subpacket and make + * sure that the buffer size is aligned to a crypto blocksize. + */ + size_t size = clamp(round_up(sp->len, 32), 2048, 65535); + void *buffer = krealloc(call->rx_dec_buffer, size, GFP_NOFS); + + if (!buffer) + return -ENOMEM; + call->rx_dec_buffer = buffer; + call->rx_dec_bsize = size; + } + + ret = -EFAULT; + if (skb_copy_bits(skb, sp->offset, call->rx_dec_buffer, sp->len) < 0) + goto err; + + call->rx_dec_offset = 0; + call->rx_dec_len = sp->len; + call->rx_dec_seq = sp->hdr.seq; + ret = call->security->verify_packet(call, skb); + if (ret < 0) + goto err; + return 0; + +err: + kfree(call->rx_dec_buffer); + call->rx_dec_buffer = NULL; + call->rx_dec_bsize = 0; + call->rx_dec_offset = 0; + call->rx_dec_len = 0; + return ret; } /* @@ -283,16 +320,21 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, if (msg) sock_recv_timestamp(msg, sock->sk, skb); - if (rx_pkt_offset == 0) { + if (call->rx_dec_seq != sp->hdr.seq || + !call->rx_dec_buffer) { ret2 = rxrpc_verify_data(call, skb); trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq, - sp->offset, sp->len, ret2); + call->rx_dec_offset, + call->rx_dec_len, ret2); if (ret2 < 0) { ret = ret2; goto out; } - rx_pkt_offset = sp->offset; - rx_pkt_len = sp->len; + } + + if (rx_pkt_offset == USHRT_MAX) { + rx_pkt_offset = call->rx_dec_offset; + rx_pkt_len = call->rx_dec_len; } else { trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq, rx_pkt_offset, rx_pkt_len, 0); @@ -304,10 +346,10 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, if (copy > remain) copy = remain; if (copy > 0) { - ret2 = skb_copy_datagram_iter(skb, rx_pkt_offset, iter, - copy); - if (ret2 < 0) { - ret = ret2; + ret2 = copy_to_iter(call->rx_dec_buffer + rx_pkt_offset, + copy, iter); + if (ret2 != copy) { + ret = -EFAULT; goto out; } @@ -328,7 +370,7 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call, /* The whole packet has been transferred. */ if (sp->hdr.flags & RXRPC_LAST_PACKET) ret = 1; - rx_pkt_offset = 0; + rx_pkt_offset = USHRT_MAX; rx_pkt_len = 0; skb = skb_peek_next(skb, &call->recvmsg_queue); diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index 04a761c79548..b40fba607a6b 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -473,8 +473,9 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxgk_header *hdr; struct krb5_buffer metadata; - unsigned int offset = sp->offset, len = sp->len; + unsigned int len = call->rx_dec_len; size_t data_offset = 0, data_len = len; + void *data = call->rx_dec_buffer, *p = data; u32 ac = 0; int ret = -ENOMEM; @@ -500,16 +501,15 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call, metadata.len = sizeof(*hdr); metadata.data = hdr; - ret = rxgk_verify_mic_skb(gk->krb5, gk->rx_Kc, &metadata, - skb, &offset, &len, &ac); + ret = rxgk_verify_mic(gk->krb5, gk->rx_Kc, &metadata, &p, &len, &ac); kfree(hdr); if (ret < 0) { if (ret != -ENOMEM) rxrpc_abort_eproto(call, skb, ac, rxgk_abort_1_verify_mic_eproto); } else { - sp->offset = offset; - sp->len = len; + call->rx_dec_offset = p - data; + call->rx_dec_len = len; } put_gk: @@ -526,56 +526,53 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call, struct sk_buff *skb) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rxgk_header hdr; - unsigned int offset = sp->offset, len = sp->len; + struct rxgk_header *hdr; + unsigned int offset = 0, len = call->rx_dec_len; + void *data = call->rx_dec_buffer, *p = data; int ret; u32 ac = 0; _enter(""); if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE, - len, sizeof(hdr)) < 0) { + len, sizeof(*hdr)) < 0) { ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, rxgk_abort_2_short_header); goto error; } - ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac); + ret = rxgk_decrypt(gk->krb5, gk->rx_enc, &p, &len, &ac); if (ret < 0) { if (ret != -ENOMEM) rxrpc_abort_eproto(call, skb, ac, rxgk_abort_2_decrypt_eproto); goto error; } + offset = p - data; - if (len < sizeof(hdr)) { + if (len < sizeof(*hdr)) { ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, rxgk_abort_2_short_header); goto error; } /* Extract the header from the skb */ - ret = skb_copy_bits(skb, offset, &hdr, sizeof(hdr)); - if (ret < 0) { - ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT, - rxgk_abort_2_short_encdata); - goto error; - } - offset += sizeof(hdr); - len -= sizeof(hdr); - - if (ntohl(hdr.epoch) != call->conn->proto.epoch || - ntohl(hdr.cid) != call->cid || - ntohl(hdr.call_number) != call->call_id || - ntohl(hdr.seq) != sp->hdr.seq || - ntohl(hdr.sec_index) != call->security_ix || - ntohl(hdr.data_len) > len) { + hdr = data + offset; + offset += sizeof(*hdr); + len -= sizeof(*hdr); + + if (ntohl(hdr->epoch) != call->conn->proto.epoch || + ntohl(hdr->cid) != call->cid || + ntohl(hdr->call_number) != call->call_id || + ntohl(hdr->seq) != sp->hdr.seq || + ntohl(hdr->sec_index) != call->security_ix || + ntohl(hdr->data_len) > len) { ret = rxrpc_abort_eproto(call, skb, RXGK_SEALEDINCON, rxgk_abort_2_short_data); goto error; } - sp->offset = offset; - sp->len = ntohl(hdr.data_len); + call->rx_dec_offset = offset; + call->rx_dec_len = ntohl(hdr->data_len); ret = 0; error: rxgk_put(gk); diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 1e257d7ab8ec..112b5366ce11 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -105,6 +105,49 @@ int rxgk_decrypt_skb(const struct krb5_enctype *krb5, return ret; } +/* + * Apply decryption and checksumming functions a flat data buffer. The data + * point and length are updated to reflect the actual content of the encrypted + * region. + */ +static inline int rxgk_decrypt(const struct krb5_enctype *krb5, + struct crypto_aead *aead, + void **_data, unsigned int *_len, + int *_error_code) +{ + struct scatterlist sg[1]; + size_t offset = 0, len = *_len; + int ret; + + sg_init_one(sg, *_data, len); + + ret = crypto_krb5_decrypt(krb5, aead, sg, 1, &offset, &len); + switch (ret) { + case 0: + if (offset & 3) { + *_error_code = RXGK_INCONSISTENCY; + ret = -EPROTO; + break; + } + *_data += offset; + *_len = len; + break; + case -EBADMSG: /* Checksum mismatch. */ + case -EPROTO: + *_error_code = RXGK_SEALEDINCON; + break; + case -EMSGSIZE: + *_error_code = RXGK_PACKETSHORT; + break; + case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ + default: + *_error_code = RXGK_INCONSISTENCY; + break; + } + + return ret; +} + /* * Check the MIC on a region of an skbuff. The offset and length are updated * to reflect the actual content of the secure region. @@ -148,3 +191,42 @@ int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, return ret; } + +/* + * Check the MIC on a flat buffer. The data pointer and length are updated to + * reflect the actual content of the secure region. + */ +static inline +int rxgk_verify_mic(const struct krb5_enctype *krb5, + struct crypto_shash *shash, + const struct krb5_buffer *metadata, + void **_data, unsigned int *_len, + u32 *_error_code) +{ + struct scatterlist sg[1]; + size_t offset = 0, len = *_len; + int ret; + + sg_init_one(sg, *_data, len); + + ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, 1, &offset, &len); + switch (ret) { + case 0: + *_data += offset; + *_len = len; + break; + case -EBADMSG: /* Checksum mismatch */ + case -EPROTO: + *_error_code = RXGK_SEALEDINCON; + break; + case -EMSGSIZE: + *_error_code = RXGK_PACKETSHORT; + break; + case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ + default: + *_error_code = RXGK_INCONSISTENCY; + break; + } + + return ret; +} diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index af24d54b6b4f..951a8913ee3f 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -430,27 +430,25 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, rxrpc_seq_t seq, struct skcipher_request *req) { - struct rxkad_level1_hdr sechdr; + struct rxkad_level1_hdr *sechdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt iv; - struct scatterlist sg[16]; - u32 data_size, buf; + struct scatterlist sg[1]; + void *data = call->rx_dec_buffer; + u32 len = sp->len, data_size, buf; u16 check; int ret; _enter(""); - if (sp->len < 8) + if (len < 8) return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_1_short_header); /* Decrypt the skbuff in-place. TODO: We really want to decrypt * directly into the target buffer. */ - sg_init_table(sg, ARRAY_SIZE(sg)); - ret = skb_to_sgvec(skb, sg, sp->offset, 8); - if (unlikely(ret < 0)) - return ret; + sg_init_one(sg, data, len); /* start the decryption afresh */ memset(&iv, 0, sizeof(iv)); @@ -464,13 +462,11 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, return ret; /* Extract the decrypted packet length */ - if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) - return rxrpc_abort_eproto(call, skb, RXKADDATALEN, - rxkad_abort_1_short_encdata); - sp->offset += sizeof(sechdr); - sp->len -= sizeof(sechdr); + sechdr = data; + call->rx_dec_offset = sizeof(*sechdr); + len -= sizeof(*sechdr); - buf = ntohl(sechdr.data_size); + buf = ntohl(sechdr->data_size); data_size = buf & 0xffff; check = buf >> 16; @@ -479,10 +475,10 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb, if (check != 0) return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_1_short_check); - if (data_size > sp->len) + if (data_size > len) return rxrpc_abort_eproto(call, skb, RXKADDATALEN, rxkad_abort_1_short_data); - sp->len = data_size; + call->rx_dec_len = data_size; _leave(" = 0 [dlen=%x]", data_size); return 0; @@ -496,43 +492,28 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, struct skcipher_request *req) { const struct rxrpc_key_token *token; - struct rxkad_level2_hdr sechdr; + struct rxkad_level2_hdr *sechdr; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt iv; - struct scatterlist _sg[4], *sg; - u32 data_size, buf; + struct scatterlist sg[1]; + void *data = call->rx_dec_buffer; + u32 len = sp->len, data_size, buf; u16 check; - int nsg, ret; + int ret; - _enter(",{%d}", sp->len); + _enter(",{%d}", len); - if (sp->len < 8) + if (len < 8) return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_2_short_header); /* Don't let the crypto algo see a misaligned length. */ - sp->len = round_down(sp->len, 8); + len = round_down(len, 8); - /* Decrypt the skbuff in-place. TODO: We really want to decrypt - * directly into the target buffer. + /* Decrypt in place in the call's decryption buffer. TODO: We really + * want to decrypt directly into the target buffer. */ - sg = _sg; - nsg = skb_shinfo(skb)->nr_frags + 1; - if (nsg <= 4) { - nsg = 4; - } else { - sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO); - if (!sg) - return -ENOMEM; - } - - sg_init_table(sg, nsg); - ret = skb_to_sgvec(skb, sg, sp->offset, sp->len); - if (unlikely(ret < 0)) { - if (sg != _sg) - kfree(sg); - return ret; - } + sg_init_one(sg, data, len); /* decrypt from the session key */ token = call->conn->key->payload.data[0]; @@ -540,11 +521,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, skcipher_request_set_sync_tfm(req, call->conn->rxkad.cipher); skcipher_request_set_callback(req, 0, NULL, NULL); - skcipher_request_set_crypt(req, sg, sg, sp->len, iv.x); + skcipher_request_set_crypt(req, sg, sg, len, iv.x); ret = crypto_skcipher_decrypt(req); skcipher_request_zero(req); - if (sg != _sg) - kfree(sg); if (ret < 0) { if (ret == -ENOMEM) return ret; @@ -553,13 +532,11 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, } /* Extract the decrypted packet length */ - if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) - return rxrpc_abort_eproto(call, skb, RXKADDATALEN, - rxkad_abort_2_short_len); - sp->offset += sizeof(sechdr); - sp->len -= sizeof(sechdr); + sechdr = data; + call->rx_dec_offset = sizeof(*sechdr); + len -= sizeof(*sechdr); - buf = ntohl(sechdr.data_size); + buf = ntohl(sechdr->data_size); data_size = buf & 0xffff; check = buf >> 16; @@ -569,17 +546,18 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb, return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON, rxkad_abort_2_short_check); - if (data_size > sp->len) + if (data_size > len) return rxrpc_abort_eproto(call, skb, RXKADDATALEN, rxkad_abort_2_short_data); - sp->len = data_size; + call->rx_dec_len = data_size; _leave(" = 0 [dlen=%x]", data_size); return 0; } /* - * Verify the security on a received packet and the subpackets therein. + * Verify the security on a received (sub)packet. If the packet needs + * modifying (e.g. decrypting), it must be copied. */ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb) { From 8bf7dbb741dde2e97f85816d2db0e6e1dc6b49c0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 29 May 2026 14:35:08 -0400 Subject: [PATCH 663/697] rxrpc: Fix RESPONSE packet verification to extract skb to a linear buffer [ Upstream commit 8bfab4b6ffc2fe92da86300728fc8c3c7ebffb56 ] This improves the fix for CVE-2026-43500. Fix the verification of RESPONSE packets to avoid the problem of overwriting a RESPONSE packet sent via splice to a local address by extracting the contents of the UDP packet into a kmalloc'd linear buffer rather than decrypting the data in place in the sk_buff (which may corrupt the original buffer). Fixes: 24481a7f5733 ("rxrpc: Fix conn-level packet handling to unshare RESPONSE packets") Reported-by: Hyunwoo Kim Closes: https://lore.kernel.org/r/afKV2zGR6rrelPC7@v4bel/ Signed-off-by: David Howells cc: Simon Horman cc: Jiayuan Chen cc: linux-afs@lists.infradead.org cc: stable@kernel.org Reviewed-by: Jeffrey Altman Tested-by: Marc Dionne Link: https://patch.msgid.link/20260515230516.2718212-4-dhowells@redhat.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/rxrpc/ar-internal.h | 7 +-- net/rxrpc/conn_event.c | 32 ++++++-------- net/rxrpc/insecure.c | 5 ++- net/rxrpc/rxgk.c | 96 +++++++++++++---------------------------- net/rxrpc/rxgk_app.c | 46 ++++++++------------ net/rxrpc/rxgk_common.h | 92 +-------------------------------------- net/rxrpc/rxkad.c | 29 +++++-------- 7 files changed, 82 insertions(+), 225 deletions(-) diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 783367eea798..98f2165159d7 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -307,15 +307,16 @@ struct rxrpc_security { struct sk_buff *challenge); /* verify a response */ - int (*verify_response)(struct rxrpc_connection *, - struct sk_buff *); + int (*verify_response)(struct rxrpc_connection *conn, + struct sk_buff *response_skb, + void *response, unsigned int len); /* clear connection security */ void (*clear)(struct rxrpc_connection *); /* Default ticket -> key decoder */ int (*default_decode_ticket)(struct rxrpc_connection *conn, struct sk_buff *skb, - unsigned int ticket_offset, unsigned int ticket_len, + void *ticket, unsigned int ticket_len, struct key **_key); }; diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c index 442414d90ba1..c96ca615b787 100644 --- a/net/rxrpc/conn_event.c +++ b/net/rxrpc/conn_event.c @@ -243,28 +243,22 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call) static int rxrpc_verify_response(struct rxrpc_connection *conn, struct sk_buff *skb) { + unsigned int len = skb->len - sizeof(struct rxrpc_wire_header); + void *buffer; int ret; - if (skb_cloned(skb) || skb_has_frag_list(skb) || - skb_has_shared_frag(skb)) { - /* Copy the packet if shared so that we can do in-place - * decryption. - */ - struct sk_buff *nskb = skb_copy(skb, GFP_NOFS); - - if (nskb) { - rxrpc_new_skb(nskb, rxrpc_skb_new_unshared); - ret = conn->security->verify_response(conn, nskb); - rxrpc_free_skb(nskb, rxrpc_skb_put_response_copy); - } else { - /* OOM - Drop the packet. */ - rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem); - ret = -ENOMEM; - } - } else { - ret = conn->security->verify_response(conn, skb); - } + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return -ENOMEM; + + ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), buffer, len); + if (ret < 0) + goto out; + + ret = conn->security->verify_response(conn, skb, buffer, len); +out: + kfree(buffer); return ret; } diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c index 7a26c6097d03..0b39046bdc61 100644 --- a/net/rxrpc/insecure.c +++ b/net/rxrpc/insecure.c @@ -54,9 +54,10 @@ static int none_sendmsg_respond_to_challenge(struct sk_buff *challenge, } static int none_verify_response(struct rxrpc_connection *conn, - struct sk_buff *skb) + struct sk_buff *response_skb, + void *response, unsigned int len) { - return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO, + return rxrpc_abort_conn(conn, response_skb, RX_PROTOCOL_ERROR, -EPROTO, rxrpc_eproto_rxnull_response); } diff --git a/net/rxrpc/rxgk.c b/net/rxrpc/rxgk.c index b40fba607a6b..1ad5f7ea3280 100644 --- a/net/rxrpc/rxgk.c +++ b/net/rxrpc/rxgk.c @@ -1084,11 +1084,12 @@ static int rxgk_sendmsg_respond_to_challenge(struct sk_buff *challenge, * unsigned int call_numbers<>; * }; */ -static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn, - const struct krb5_enctype *krb5, - struct sk_buff *skb, - __be32 *p, __be32 *end) +static int rxgk_verify_authenticator(struct rxrpc_connection *conn, + const struct krb5_enctype *krb5, + struct sk_buff *skb, + void *auth, unsigned int auth_len) { + __be32 *p = auth, *end = auth + auth_len; u32 app_len, call_count, level, epoch, cid, i; _enter(""); @@ -1151,37 +1152,6 @@ static int rxgk_do_verify_authenticator(struct rxrpc_connection *conn, return 0; } -/* - * Extract the authenticator and verify it. - */ -static int rxgk_verify_authenticator(struct rxrpc_connection *conn, - const struct krb5_enctype *krb5, - struct sk_buff *skb, - unsigned int auth_offset, unsigned int auth_len) -{ - void *auth; - __be32 *p; - int ret; - - auth = kmalloc(auth_len, GFP_NOFS); - if (!auth) - return -ENOMEM; - - ret = skb_copy_bits(skb, auth_offset, auth, auth_len); - if (ret < 0) { - ret = rxrpc_abort_conn(conn, skb, RXGK_NOTAUTH, -EPROTO, - rxgk_abort_resp_short_auth); - goto error; - } - - p = auth; - ret = rxgk_do_verify_authenticator(conn, krb5, skb, p, - p + auth_len / sizeof(*p)); -error: - kfree(auth); - return ret; -} - /* * Verify a response. * @@ -1192,49 +1162,45 @@ static int rxgk_verify_authenticator(struct rxrpc_connection *conn, * }; */ static int rxgk_verify_response(struct rxrpc_connection *conn, - struct sk_buff *skb) + struct sk_buff *skb, + void *buffer, unsigned int len) { const struct krb5_enctype *krb5; struct rxrpc_key_token *token; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); - struct rxgk_response rhdr; + struct rxgk_response *rhdr; struct rxgk_context *gk; struct key *key = NULL; - unsigned int offset = sizeof(struct rxrpc_wire_header); - unsigned int len = skb->len - sizeof(struct rxrpc_wire_header); - unsigned int token_offset, token_len; - unsigned int auth_offset, auth_len; + unsigned int resp_token_len, auth_len; + void *resp_token, *auth; __be32 xauth_len; int ret, ec; _enter("{%d}", conn->debug_id); /* Parse the RXGK_Response object */ - if (sizeof(rhdr) + sizeof(__be32) > len) - goto short_packet; - - if (skb_copy_bits(skb, offset, &rhdr, sizeof(rhdr)) < 0) + if (len < sizeof(*rhdr) + sizeof(__be32)) goto short_packet; - offset += sizeof(rhdr); - len -= sizeof(rhdr); - - token_offset = offset; - token_len = ntohl(rhdr.token_len); - if (token_len > len || - xdr_round_up(token_len) + sizeof(__be32) > len) + rhdr = buffer; + buffer += sizeof(*rhdr); + len -= sizeof(*rhdr); + + resp_token = buffer; + resp_token_len = ntohl(rhdr->token_len); + if (resp_token_len > len || + xdr_round_up(resp_token_len) + sizeof(__be32) > len) goto short_packet; - trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, token_len); + trace_rxrpc_rx_response(conn, sp->hdr.serial, 0, sp->hdr.cksum, resp_token_len); - offset += xdr_round_up(token_len); - len -= xdr_round_up(token_len); + buffer += xdr_round_up(resp_token_len); + len -= xdr_round_up(resp_token_len); - if (skb_copy_bits(skb, offset, &xauth_len, sizeof(xauth_len)) < 0) - goto short_packet; - offset += sizeof(xauth_len); + xauth_len = *(__be32 *)buffer; + buffer += sizeof(xauth_len); len -= sizeof(xauth_len); - auth_offset = offset; + auth = buffer; auth_len = ntohl(xauth_len); if (auth_len > len) goto short_packet; @@ -1249,7 +1215,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, * to the app to deal with - which might mean a round trip to * userspace. */ - ret = rxgk_extract_token(conn, skb, token_offset, token_len, &key); + ret = rxgk_extract_token(conn, skb, resp_token, resp_token_len, &key); if (ret < 0) goto out; @@ -1263,7 +1229,7 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, */ token = key->payload.data[0]; conn->security_level = token->rxgk->level; - conn->rxgk.start_time = __be64_to_cpu(rhdr.start_time); + conn->rxgk.start_time = __be64_to_cpu(rhdr->start_time); gk = rxgk_generate_transport_key(conn, token->rxgk, sp->hdr.cksum, GFP_NOFS); if (IS_ERR(gk)) { @@ -1273,18 +1239,18 @@ static int rxgk_verify_response(struct rxrpc_connection *conn, krb5 = gk->krb5; - trace_rxrpc_rx_response(conn, sp->hdr.serial, krb5->etype, sp->hdr.cksum, token_len); + trace_rxrpc_rx_response(conn, sp->hdr.serial, krb5->etype, sp->hdr.cksum, + resp_token_len); /* Decrypt, parse and verify the authenticator. */ - ret = rxgk_decrypt_skb(krb5, gk->resp_enc, skb, - &auth_offset, &auth_len, &ec); + ret = rxgk_decrypt(krb5, gk->resp_enc, &auth, &auth_len, &ec); if (ret < 0) { rxrpc_abort_conn(conn, skb, RXGK_SEALEDINCON, ret, rxgk_abort_resp_auth_dec); goto out_gk; } - ret = rxgk_verify_authenticator(conn, krb5, skb, auth_offset, auth_len); + ret = rxgk_verify_authenticator(conn, krb5, skb, auth, auth_len); if (ret < 0) goto out_gk; diff --git a/net/rxrpc/rxgk_app.c b/net/rxrpc/rxgk_app.c index 0ef2a29eb695..200a30064fae 100644 --- a/net/rxrpc/rxgk_app.c +++ b/net/rxrpc/rxgk_app.c @@ -40,7 +40,7 @@ * }; */ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, - unsigned int ticket_offset, unsigned int ticket_len, + void *buffer, unsigned int ticket_len, struct key **_key) { struct rxrpc_key_token *token; @@ -49,7 +49,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, size_t pre_ticket_len, payload_len; unsigned int klen, enctype; void *payload, *ticket; - __be32 *t, *p, *q, tmp[2]; + __be32 *t, *p, *q, *tmp; int ret; _enter(""); @@ -59,10 +59,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, rxgk_abort_resp_short_yfs_tkt); /* Get the session key length */ - ret = skb_copy_bits(skb, ticket_offset, tmp, sizeof(tmp)); - if (ret < 0) - return rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, - rxgk_abort_resp_short_yfs_klen); + tmp = buffer; enctype = ntohl(tmp[0]); klen = ntohl(tmp[1]); @@ -84,12 +81,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, * it. */ ticket = payload + pre_ticket_len; - ret = skb_copy_bits(skb, ticket_offset, ticket, ticket_len); - if (ret < 0) { - ret = rxrpc_abort_conn(conn, skb, RXGK_INCONSISTENCY, -EPROTO, - rxgk_abort_resp_short_yfs_tkt); - goto error; - } + memcpy(ticket, buffer, ticket_len); /* Fill out the form header. */ p = payload; @@ -131,7 +123,7 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, goto error; } - /* Ticket read in with skb_copy_bits above */ + /* Ticket appended above. */ q += xdr_round_up(ticket_len) / 4; if (WARN_ON((unsigned long)q - (unsigned long)payload != payload_len)) { ret = -EIO; @@ -182,14 +174,15 @@ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, * [tools.ietf.org/html/draft-wilkinson-afs3-rxgk-afs-08 sec 6.1] */ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, - unsigned int token_offset, unsigned int token_len, + void *token, unsigned int token_len, struct key **_key) { const struct krb5_enctype *krb5; const struct krb5_buffer *server_secret; struct crypto_aead *token_enc = NULL; struct key *server_key; - unsigned int ticket_offset, ticket_len; + unsigned int ticket_len; + void *ticket; u32 kvno, enctype; int ret, ec = 0; @@ -197,24 +190,23 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, __be32 kvno; __be32 enctype; __be32 token_len; - } container; + } *container; - if (token_len < sizeof(container)) + if (token_len < sizeof(*container)) goto short_packet; /* Decode the RXGK_TokenContainer object. This tells us which server * key we should be using. We can then fetch the key, get the secret * and set up the crypto to extract the token. */ - if (skb_copy_bits(skb, token_offset, &container, sizeof(container)) < 0) - goto short_packet; + container = token; + token += sizeof(*container); - kvno = ntohl(container.kvno); - enctype = ntohl(container.enctype); - ticket_len = ntohl(container.token_len); - ticket_offset = token_offset + sizeof(container); + kvno = ntohl(container->kvno); + enctype = ntohl(container->enctype); + ticket_len = ntohl(container->token_len); - if (ticket_len > xdr_round_down(token_len - sizeof(container))) + if (ticket_len > xdr_round_down(token_len - sizeof(*container))) goto short_packet; _debug("KVNO %u", kvno); @@ -237,8 +229,8 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, * gain access to K0, from which we can derive the transport key and * thence decode the authenticator. */ - ret = rxgk_decrypt_skb(krb5, token_enc, skb, - &ticket_offset, &ticket_len, &ec); + ticket = token; + ret = rxgk_decrypt(krb5, token_enc, &ticket, &ticket_len, &ec); crypto_free_aead(token_enc); token_enc = NULL; if (ret < 0) { @@ -248,7 +240,7 @@ int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, return ret; } - ret = conn->security->default_decode_ticket(conn, skb, ticket_offset, + ret = conn->security->default_decode_ticket(conn, skb, ticket, ticket_len, _key); if (ret < 0) goto cant_get_token; diff --git a/net/rxrpc/rxgk_common.h b/net/rxrpc/rxgk_common.h index 112b5366ce11..3deed5863f5a 100644 --- a/net/rxrpc/rxgk_common.h +++ b/net/rxrpc/rxgk_common.h @@ -41,10 +41,10 @@ struct rxgk_context { * rxgk_app.c */ int rxgk_yfs_decode_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, - unsigned int ticket_offset, unsigned int ticket_len, + void *ticket, unsigned int ticket_len, struct key **_key); int rxgk_extract_token(struct rxrpc_connection *conn, struct sk_buff *skb, - unsigned int token_offset, unsigned int token_len, + void *token, unsigned int token_len, struct key **_key); /* @@ -61,50 +61,6 @@ int rxgk_set_up_token_cipher(const struct krb5_buffer *server_key, const struct krb5_enctype **_krb5, gfp_t gfp); -/* - * Apply decryption and checksumming functions to part of an skbuff. The - * offset and length are updated to reflect the actual content of the encrypted - * region. - */ -static inline -int rxgk_decrypt_skb(const struct krb5_enctype *krb5, - struct crypto_aead *aead, - struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len, - int *_error_code) -{ - struct scatterlist sg[16]; - size_t offset = 0, len = *_len; - int nr_sg, ret; - - sg_init_table(sg, ARRAY_SIZE(sg)); - nr_sg = skb_to_sgvec(skb, sg, *_offset, len); - if (unlikely(nr_sg < 0)) - return nr_sg; - - ret = crypto_krb5_decrypt(krb5, aead, sg, nr_sg, - &offset, &len); - switch (ret) { - case 0: - *_offset += offset; - *_len = len; - break; - case -EBADMSG: /* Checksum mismatch. */ - case -EPROTO: - *_error_code = RXGK_SEALEDINCON; - break; - case -EMSGSIZE: - *_error_code = RXGK_PACKETSHORT; - break; - case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ - default: - *_error_code = RXGK_INCONSISTENCY; - break; - } - - return ret; -} - /* * Apply decryption and checksumming functions a flat data buffer. The data * point and length are updated to reflect the actual content of the encrypted @@ -148,50 +104,6 @@ static inline int rxgk_decrypt(const struct krb5_enctype *krb5, return ret; } -/* - * Check the MIC on a region of an skbuff. The offset and length are updated - * to reflect the actual content of the secure region. - */ -static inline -int rxgk_verify_mic_skb(const struct krb5_enctype *krb5, - struct crypto_shash *shash, - const struct krb5_buffer *metadata, - struct sk_buff *skb, - unsigned int *_offset, unsigned int *_len, - u32 *_error_code) -{ - struct scatterlist sg[16]; - size_t offset = 0, len = *_len; - int nr_sg, ret; - - sg_init_table(sg, ARRAY_SIZE(sg)); - nr_sg = skb_to_sgvec(skb, sg, *_offset, len); - if (unlikely(nr_sg < 0)) - return nr_sg; - - ret = crypto_krb5_verify_mic(krb5, shash, metadata, sg, nr_sg, - &offset, &len); - switch (ret) { - case 0: - *_offset += offset; - *_len = len; - break; - case -EBADMSG: /* Checksum mismatch */ - case -EPROTO: - *_error_code = RXGK_SEALEDINCON; - break; - case -EMSGSIZE: - *_error_code = RXGK_PACKETSHORT; - break; - case -ENOPKG: /* Would prefer RXGK_BADETYPE, but not available for YFS. */ - default: - *_error_code = RXGK_INCONSISTENCY; - break; - } - - return ret; -} - /* * Check the MIC on a flat buffer. The data pointer and length are updated to * reflect the actual content of the secure region. diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 951a8913ee3f..6fbd883401ac 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -963,7 +963,6 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, *_expiry = 0; ASSERT(server_key->payload.data[0] != NULL); - ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); memcpy(&iv, &server_key->payload.data[2], sizeof(iv)); @@ -1112,14 +1111,15 @@ static int rxkad_decrypt_response(struct rxrpc_connection *conn, * verify a response */ static int rxkad_verify_response(struct rxrpc_connection *conn, - struct sk_buff *skb) + struct sk_buff *skb, + void *buffer, unsigned int len) { struct rxkad_response *response; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt session_key; struct key *server_key; time64_t expiry; - void *ticket = NULL; + void *ticket; u32 version, kvno, ticket_len, level; __be32 csum; int ret, i; @@ -1142,13 +1142,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, } } - ret = -ENOMEM; - response = kzalloc(sizeof(struct rxkad_response), GFP_NOFS); - if (!response) - goto error; - - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header), - response, sizeof(*response)) < 0) { + response = buffer; + if (len < sizeof(*response)) { ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, rxkad_abort_resp_short); goto error; @@ -1160,6 +1155,9 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len); + buffer += sizeof(*response); + len -= sizeof(*response); + if (version != RXKAD_VERSION) { ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO, rxkad_abort_resp_version); @@ -1179,13 +1177,8 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, } /* extract the kerberos ticket and decrypt and decode it */ - ret = -ENOMEM; - ticket = kmalloc(ticket_len, GFP_NOFS); - if (!ticket) - goto error; - - if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response), - ticket, ticket_len) < 0) { + ticket = buffer; + if (ticket_len > len) { ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO, rxkad_abort_resp_short_tkt); goto error; @@ -1265,8 +1258,6 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno); error: - kfree(ticket); - kfree(response); key_put(server_key); _leave(" = %d", ret); return ret; From 0acba63d7d462e87d362f650e79b9475d1ec703a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 29 May 2026 15:23:48 -0400 Subject: [PATCH 664/697] serdev: Provide a bustype shutdown function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 6d71c62b13c33ea858ab298fe20beaec5736edc7 ] To prepare serdev driver to migrate away from struct device_driver::shutdown (and then eventually remove that callback) create a serdev driver shutdown callback and migration code to keep the existing behaviour. Note this introduces a warning for each driver at register time that isn't converted yet to that callback. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/ab518883e3ed0976a19cb5b5b5faf42bd3a655b7.1765526117.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 375ba7484132 ("Bluetooth: hci_qca: Convert timeout from jiffies to ms") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 21 +++++++++++++++++++++ include/linux/serdev.h | 1 + 2 files changed, 22 insertions(+) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index b33e708cb245..40eedc15277c 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -414,11 +414,21 @@ static void serdev_drv_remove(struct device *dev) sdrv->remove(to_serdev_device(dev)); } +static void serdev_drv_shutdown(struct device *dev) +{ + const struct serdev_device_driver *sdrv = + to_serdev_device_driver(dev->driver); + + if (dev->driver && sdrv->shutdown) + sdrv->shutdown(to_serdev_device(dev)); +} + static const struct bus_type serdev_bus_type = { .name = "serial", .match = serdev_device_match, .probe = serdev_drv_probe, .remove = serdev_drv_remove, + .shutdown = serdev_drv_shutdown, }; /** @@ -814,6 +824,14 @@ void serdev_controller_remove(struct serdev_controller *ctrl) } EXPORT_SYMBOL_GPL(serdev_controller_remove); +static void serdev_legacy_shutdown(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct device_driver *driver = dev->driver; + + driver->shutdown(dev); +} + /** * __serdev_device_driver_register() - Register client driver with serdev core * @sdrv: client driver to be associated with client-device. @@ -830,6 +848,9 @@ int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct mo /* force drivers to async probe so I/O is possible in probe */ sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; + if (!sdrv->shutdown && sdrv->driver.shutdown) + sdrv->shutdown = serdev_legacy_shutdown; + return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(__serdev_device_driver_register); diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 34562eb99931..5654c58eb73c 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -65,6 +65,7 @@ struct serdev_device_driver { struct device_driver driver; int (*probe)(struct serdev_device *); void (*remove)(struct serdev_device *); + void (*shutdown)(struct serdev_device *); }; static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d) From 8264178afb5c874ed986245e026d6629427d638b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Fri, 29 May 2026 15:23:49 -0400 Subject: [PATCH 665/697] Bluetooth: hci_qca: Migrate to serdev specific shutdown function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 12a6a5726c515455935982429ac35dee2307233d ] This saves a cast in the driver. The motivation is stop using the callback .shutdown in qca_serdev_driver.driver to make it possible to drop that. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/261a3384e25c4837d4efee87958805f15d7d4e3c.1765526117.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman Stable-dep-of: 375ba7484132 ("Bluetooth: hci_qca: Convert timeout from jiffies to ms") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_qca.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index acb75199ba49..f64b1c4b8fc5 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -2567,11 +2567,10 @@ static void qca_serdev_remove(struct serdev_device *serdev) hci_uart_unregister_device(&qcadev->serdev_hu); } -static void qca_serdev_shutdown(struct device *dev) +static void qca_serdev_shutdown(struct serdev_device *serdev) { int ret; int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS); - struct serdev_device *serdev = to_serdev_device(dev); struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); struct hci_uart *hu = &qcadev->serdev_hu; struct hci_dev *hdev = hu->hdev; @@ -2793,11 +2792,11 @@ static void hciqca_coredump(struct device *dev) static struct serdev_device_driver qca_serdev_driver = { .probe = qca_serdev_probe, .remove = qca_serdev_remove, + .shutdown = qca_serdev_shutdown, .driver = { .name = "hci_uart_qca", .of_match_table = of_match_ptr(qca_bluetooth_of_match), .acpi_match_table = ACPI_PTR(qca_bluetooth_acpi_match), - .shutdown = qca_serdev_shutdown, .pm = &qca_pm_ops, #ifdef CONFIG_DEV_COREDUMP .coredump = hciqca_coredump, From c5e7d4865292459ba216b12001b70577da0efc4b Mon Sep 17 00:00:00 2001 From: Shuai Zhang Date: Fri, 29 May 2026 15:23:50 -0400 Subject: [PATCH 666/697] Bluetooth: hci_qca: Convert timeout from jiffies to ms [ Upstream commit 375ba7484132662a4a8c7547d088fb6275c00282 ] Since the timer uses jiffies as its unit rather than ms, the timeout value must be converted from ms to jiffies when configuring the timer. Otherwise, the intended 8s timeout is incorrectly set to approximately 33s. To improve readability, embed msecs_to_jiffies() directly in the macro definitions and drop the _MS suffix from macros that now yield jiffies values: MEMDUMP_TIMEOUT, FW_DOWNLOAD_TIMEOUT, IBS_DISABLE_SSR_TIMEOUT, CMD_TRANS_TIMEOUT, and IBS_BTSOC_TX_IDLE_TIMEOUT. IBS_WAKE_RETRANS_TIMEOUT_MS and IBS_HOST_TX_IDLE_TIMEOUT_MS are intentionally left unchanged. Their values are stored in the struct fields wake_retrans and tx_idle_delay, which hold ms values at runtime and can be modified via debugfs. The msecs_to_jiffies() conversion happens at each call site against the field value, so it cannot be embedded in the macro. Wake timer depends on commit c347ca17d62a Cc: stable@vger.kernel.org Fixes: d841502c79e3 ("Bluetooth: hci_qca: Collect controller memory dump during SSR") Reviewed-by: Paul Menzel Acked-by: Bartosz Golaszewski Signed-off-by: Shuai Zhang Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_qca.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index f64b1c4b8fc5..d37a51101ca7 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -48,13 +48,12 @@ #define HCI_MAX_IBS_SIZE 10 #define IBS_WAKE_RETRANS_TIMEOUT_MS 100 -#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 200 +#define IBS_BTSOC_TX_IDLE_TIMEOUT msecs_to_jiffies(200) #define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000 -#define CMD_TRANS_TIMEOUT_MS 100 -#define MEMDUMP_TIMEOUT_MS 8000 -#define IBS_DISABLE_SSR_TIMEOUT_MS \ - (MEMDUMP_TIMEOUT_MS + FW_DOWNLOAD_TIMEOUT_MS) -#define FW_DOWNLOAD_TIMEOUT_MS 3000 +#define CMD_TRANS_TIMEOUT msecs_to_jiffies(100) +#define MEMDUMP_TIMEOUT msecs_to_jiffies(8000) +#define FW_DOWNLOAD_TIMEOUT msecs_to_jiffies(3000) +#define IBS_DISABLE_SSR_TIMEOUT (MEMDUMP_TIMEOUT + FW_DOWNLOAD_TIMEOUT) /* susclk rate */ #define SUSCLK_RATE_32KHZ 32768 @@ -1091,7 +1090,7 @@ static void qca_controller_memdump(struct work_struct *work) queue_delayed_work(qca->workqueue, &qca->ctrl_memdump_timeout, - msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)); + MEMDUMP_TIMEOUT); skb_pull(skb, sizeof(qca_memdump->ram_dump_size)); qca_memdump->current_seq_no = 0; qca_memdump->received_dump = 0; @@ -1364,7 +1363,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) if (hu->serdev) serdev_device_wait_until_sent(hu->serdev, - msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); + CMD_TRANS_TIMEOUT); /* Give the controller time to process the request */ switch (qca_soc_type(hu)) { @@ -1396,8 +1395,8 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed) static int qca_send_power_pulse(struct hci_uart *hu, bool on) { + int timeout = CMD_TRANS_TIMEOUT; int ret; - int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS); u8 cmd = on ? QCA_WCN3990_POWERON_PULSE : QCA_WCN3990_POWEROFF_PULSE; /* These power pulses are single byte command which are sent @@ -1602,7 +1601,7 @@ static void qca_wait_for_dump_collection(struct hci_dev *hdev) struct qca_data *qca = hu->priv; wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION, - TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS); + TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT); clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); } @@ -2570,7 +2569,7 @@ static void qca_serdev_remove(struct serdev_device *serdev) static void qca_serdev_shutdown(struct serdev_device *serdev) { int ret; - int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS); + int timeout = CMD_TRANS_TIMEOUT; struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); struct hci_uart *hu = &qcadev->serdev_hu; struct hci_dev *hdev = hu->hdev; @@ -2627,7 +2626,7 @@ static int __maybe_unused qca_suspend(struct device *dev) bool tx_pending = false; int ret = 0; u8 cmd; - u32 wait_timeout = 0; + unsigned long wait_timeout = 0; set_bit(QCA_SUSPENDING, &qca->flags); @@ -2648,15 +2647,15 @@ static int __maybe_unused qca_suspend(struct device *dev) if (test_bit(QCA_IBS_DISABLED, &qca->flags) || test_bit(QCA_SSR_TRIGGERED, &qca->flags)) { wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ? - IBS_DISABLE_SSR_TIMEOUT_MS : - FW_DOWNLOAD_TIMEOUT_MS; + IBS_DISABLE_SSR_TIMEOUT : + FW_DOWNLOAD_TIMEOUT; /* QCA_IBS_DISABLED flag is set to true, During FW download * and during memory dump collection. It is reset to false, * After FW download complete. */ wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED, - TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout)); + TASK_UNINTERRUPTIBLE, wait_timeout); if (test_bit(QCA_IBS_DISABLED, &qca->flags)) { bt_dev_err(hu->hdev, "SSR or FW download time out"); @@ -2708,7 +2707,7 @@ static int __maybe_unused qca_suspend(struct device *dev) if (tx_pending) { serdev_device_wait_until_sent(hu->serdev, - msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); + CMD_TRANS_TIMEOUT); serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu); } @@ -2717,7 +2716,7 @@ static int __maybe_unused qca_suspend(struct device *dev) */ ret = wait_event_interruptible_timeout(qca->suspend_wait_q, qca->rx_ibs_state == HCI_IBS_RX_ASLEEP, - msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS)); + IBS_BTSOC_TX_IDLE_TIMEOUT); if (ret == 0) { ret = -ETIMEDOUT; goto error; From b2beed6ad14937966768aa8e423ea7cabe298037 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Fri, 29 May 2026 20:20:38 -0400 Subject: [PATCH 667/697] selftests: mptcp: drop nanoseconds width specifier [ Upstream commit 01ff78e4b3d98689184c52d97f9575dfbdc3b10f ] Using the format specifier +%s%3N with GNU date is honoured, and only prints 3 digits of the nanoseconds portion of the seconds since epoch, which corresponds to the milliseconds. The uutils implementation of date currently does not honour this, and always prints all 9 digits. This is a known issue [1], but can be worked around by adapting this test to use nanoseconds instead of microseconds, and then divide it by 1e6. This fix is similar to what has been done on systemd side [2], and it is needed to run the selftests on Ubuntu 26.04, containing uutils 0.8.0. Note that the Fixes tag is there even if this patch doesn't fix an issue in the kernel selftests, but it is useful for those using uutils 0.8.0. Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") Cc: stable@vger.kernel.org Link: https://github.com/uutils/coreutils/issues/11658 [1] Link: https://github.com/systemd/systemd/pull/41627 [2] Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-6-701e96419f2f@kernel.org Signed-off-by: Paolo Abeni [ kept `timeout ${timeout_test}` wrapper in do_transfer() ] Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- tools/testing/selftests/net/mptcp/mptcp_connect.sh | 6 +++--- tools/testing/selftests/net/mptcp/mptcp_lib.sh | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh index 9b7b93f8eb0c..fa7abcaa1414 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh @@ -415,7 +415,7 @@ do_transfer() mptcp_lib_wait_local_port_listen "${listener_ns}" "${port}" local start - start=$(date +%s%3N) + start=$(date +%s%N) timeout ${timeout_test} \ ip netns exec ${connector_ns} \ ./mptcp_connect -t ${timeout_poll} -p $port -s ${cl_proto} \ @@ -428,7 +428,7 @@ do_transfer() local rets=$? local stop - stop=$(date +%s%3N) + stop=$(date +%s%N) if $capture; then sleep 1 @@ -444,7 +444,7 @@ do_transfer() fi local duration - duration=$((stop-start)) + duration=$(((stop-start) / 1000000)) printf "(duration %05sms) " "${duration}" if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then mptcp_lib_pr_fail "client exit code $retc, server $rets" diff --git a/tools/testing/selftests/net/mptcp/mptcp_lib.sh b/tools/testing/selftests/net/mptcp/mptcp_lib.sh index 8b5c2285ad73..a64133cb82f7 100644 --- a/tools/testing/selftests/net/mptcp/mptcp_lib.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_lib.sh @@ -28,7 +28,7 @@ declare -rx MPTCP_LIB_AF_INET6=10 MPTCP_LIB_SUBTESTS=() MPTCP_LIB_SUBTESTS_DUPLICATED=0 MPTCP_LIB_SUBTEST_FLAKY=0 -MPTCP_LIB_SUBTESTS_LAST_TS_MS= +MPTCP_LIB_SUBTESTS_LAST_TS_NS= MPTCP_LIB_TEST_COUNTER=0 MPTCP_LIB_TEST_FORMAT="%02u %-50s" MPTCP_LIB_IP_MPTCP=0 @@ -227,7 +227,7 @@ mptcp_lib_kversion_ge() { } mptcp_lib_subtests_last_ts_reset() { - MPTCP_LIB_SUBTESTS_LAST_TS_MS="$(date +%s%3N)" + MPTCP_LIB_SUBTESTS_LAST_TS_NS="$(date +%s%N)" } mptcp_lib_subtests_last_ts_reset @@ -246,7 +246,7 @@ __mptcp_lib_result_check_duplicated() { __mptcp_lib_result_add() { local result="${1}" local time="time=" - local ts_prev_ms + local ts_prev_ns shift local id=$((${#MPTCP_LIB_SUBTESTS[@]} + 1)) @@ -256,9 +256,9 @@ __mptcp_lib_result_add() { # not to add two '#' [[ "${*}" != *"#"* ]] && time="# ${time}" - ts_prev_ms="${MPTCP_LIB_SUBTESTS_LAST_TS_MS}" + ts_prev_ns="${MPTCP_LIB_SUBTESTS_LAST_TS_NS}" mptcp_lib_subtests_last_ts_reset - time+="$((MPTCP_LIB_SUBTESTS_LAST_TS_MS - ts_prev_ms))ms" + time+="$(((MPTCP_LIB_SUBTESTS_LAST_TS_NS - ts_prev_ns) / 1000000))ms" MPTCP_LIB_SUBTESTS+=("${result} ${id} - ${KSFT_TEST}: ${*} ${time}") } From 134c517dfa63203287b2aad6558017f42435a02e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 19:22:54 -0400 Subject: [PATCH 668/697] net: devmem: reject dma-buf bind with non-page-aligned size or SG length [ Upstream commit 4eb82ba543421e9e38cc14e4e82058b78850df50 ] net_devmem_bind_dmabuf() trusts dmabuf->size and sg_dma_len() to be PAGE_SIZE multiples without checking: - tx_vec is sized dmabuf->size / PAGE_SIZE, and net_devmem_get_niov_at() only bounds-checks virt_addr < dmabuf->size before indexing tx_vec[virt_addr / PAGE_SIZE]. With size = N*PAGE_SIZE + r (1 <= r < PAGE_SIZE), sendmsg() at iov_base = N*PAGE_SIZE passes the bound check and reads tx_vec[N] -- one past. - owner->area.num_niovs = len / PAGE_SIZE while gen_pool_add_owner() covers the full byte len, so a non-page-multiple non-final sg desyncs num_niovs from the gen_pool region for every later sg, on both RX and TX. dma-buf does not require page-aligned sizes, so the bind path has to enforce what its own indexing assumes. Reject both with -EINVAL. The size check is TX-only (only tx_vec is sized off dmabuf->size); the SG-length check covers both directions. Fixes: bd61848900bf ("net: devmem: Implement TX path") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Bobby Eshleman Acked-by: Stanislav Fomichev Reviewed-by: Mina Almasry Link: https://patch.msgid.link/20260519203530.66310-1-devnexen@gmail.com Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/core/devmem.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/core/devmem.c b/net/core/devmem.c index 448f6582ac1a..16ce197cb0fa 100644 --- a/net/core/devmem.c +++ b/net/core/devmem.c @@ -232,6 +232,11 @@ net_devmem_bind_dmabuf(struct net_device *dev, } if (direction == DMA_TO_DEVICE) { + if (!IS_ALIGNED(dmabuf->size, PAGE_SIZE)) { + err = -EINVAL; + NL_SET_ERR_MSG(extack, "TX dma-buf size must be a multiple of PAGE_SIZE"); + goto err_unmap; + } binding->tx_vec = kvmalloc_array(dmabuf->size / PAGE_SIZE, sizeof(struct net_iov *), GFP_KERNEL); @@ -259,6 +264,12 @@ net_devmem_bind_dmabuf(struct net_device *dev, size_t len = sg_dma_len(sg); struct net_iov *niov; + if (!IS_ALIGNED(len, PAGE_SIZE)) { + err = -EINVAL; + NL_SET_ERR_MSG(extack, "dma-buf SG length must be PAGE_SIZE aligned"); + goto err_free_chunks; + } + owner = kzalloc_node(sizeof(*owner), GFP_KERNEL, dev_to_node(&dev->dev)); if (!owner) { From c67f986fc02ce7dcf4616a6a3221a25a44ca067f Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Sat, 30 May 2026 07:48:08 -0400 Subject: [PATCH 669/697] mptcp: handle first subflow closing consistently [ Upstream commit 0eeb372deebce6c25b9afc09e35d6c75a744299a ] Currently, as soon as the PM closes a subflow, the msk stops accepting data from it, even if the TCP socket could be still formally open in the incoming direction, with the notable exception of the first subflow. The root cause of such behavior is that code currently piggy back two separate semantic on the subflow->disposable bit: the subflow context must be released and that the subflow must stop accepting incoming data. The first subflow is never disposed, so it also never stop accepting incoming data. Use a separate bit to mark the latter status and set such bit in __mptcp_close_ssk() for all subflows. Beyond making per subflow behaviour more consistent this will also simplify the next patch. Signed-off-by: Paolo Abeni Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251121-net-next-mptcp-memcg-backlog-imp-v1-11-1f34b6c1e0b1@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: 50c2d91c5dfa ("mptcp: do not drop partial packets") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 14 +++++++++----- net/mptcp/protocol.h | 3 ++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 1dbe6c8a8b8c..0dde73543da0 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -854,10 +854,10 @@ void mptcp_data_ready(struct sock *sk, struct sock *ssk) struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); /* The peer can send data while we are shutting down this - * subflow at msk destruction time, but we must avoid enqueuing + * subflow at subflow destruction time, but we must avoid enqueuing * more data to the msk receive queue */ - if (unlikely(subflow->disposable)) + if (unlikely(subflow->closing)) return; mptcp_data_lock(sk); @@ -2455,6 +2455,13 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, struct mptcp_sock *msk = mptcp_sk(sk); bool dispose_it, need_push = false; + /* Do not pass RX data to the msk, even if the subflow socket is not + * going to be freed (i.e. even for the first subflow on graceful + * subflow close. + */ + lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); + subflow->closing = 1; + /* If the first subflow moved to a close state before accept, e.g. due * to an incoming reset or listener shutdown, the subflow socket is * already deleted by inet_child_forget() and the mptcp socket can't @@ -2465,7 +2472,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, /* ensure later check in mptcp_worker() will dispose the msk */ sock_set_flag(sk, SOCK_DEAD); mptcp_set_close_tout(sk, tcp_jiffies32 - (mptcp_close_timeout(sk) + 1)); - lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); mptcp_subflow_drop_ctx(ssk); goto out_release; } @@ -2474,8 +2480,6 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, if (dispose_it) list_del(&subflow->node); - lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); - if (subflow->send_fastclose && ssk->sk_state != TCP_CLOSE) tcp_set_state(ssk, TCP_CLOSE); diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 5a03c8824ab6..feacb1feab47 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -537,12 +537,13 @@ struct mptcp_subflow_context { send_infinite_map : 1, remote_key_valid : 1, /* received the peer key from */ disposable : 1, /* ctx can be free at ulp release time */ + closing : 1, /* must not pass rx data to msk anymore */ stale : 1, /* unable to snd/rcv data, do not use for xmit */ valid_csum_seen : 1, /* at least one csum validated */ is_mptfo : 1, /* subflow is doing TFO */ close_event_done : 1, /* has done the post-closed part */ mpc_drop : 1, /* the MPC option has been dropped in a rtx */ - __unused : 9; + __unused : 8; bool data_avail; bool scheduled; bool pm_listener; /* a listener managed by the kernel PM? */ From a84164847b1e0a106ebc46821e5d2f9b775c9dc8 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Sat, 30 May 2026 07:48:09 -0400 Subject: [PATCH 670/697] mptcp: borrow forward memory from subflow [ Upstream commit 9db5b3cec4ec1c0cd3239689f5c8653d691a1754 ] In the MPTCP receive path, we release the subflow allocated fwd memory just to allocate it again shortly after for the msk. That could increases the failures chances, especially when we will add backlog processing, with other actions could consume the just released memory before the msk socket has a chance to do the rcv allocation. Replace the skb_orphan() call with an open-coded variant that explicitly borrows, the fwd memory from the subflow socket instead of releasing it. The borrowed memory does not have PAGE_SIZE granularity; rounding to the page size will make the fwd allocated memory higher than what is strictly required and could make the incoming subflow fwd mem consistently negative. Instead, keep track of the accumulated frag and borrow the full page at subflow close time. This allow removing the last drop in the TCP to MPTCP transition and the associated, now unused, MIB. Signed-off-by: Paolo Abeni Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251121-net-next-mptcp-memcg-backlog-imp-v1-12-1f34b6c1e0b1@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: 50c2d91c5dfa ("mptcp: do not drop partial packets") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/fastopen.c | 4 +++- net/mptcp/mib.c | 1 - net/mptcp/mib.h | 1 - net/mptcp/protocol.c | 23 +++++++++++++++-------- net/mptcp/protocol.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index c8aa5426f1af..082c46c0f50e 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -33,7 +33,8 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf /* dequeue the skb from sk receive queue */ __skb_unlink(skb, &ssk->sk_receive_queue); skb_ext_reset(skb); - skb_orphan(skb); + + mptcp_subflow_lend_fwdmem(subflow, skb); /* We copy the fastopen data, but that don't belong to the mptcp sequence * space, need to offset it in the subflow sequence, see mptcp_subflow_get_map_offset() @@ -52,6 +53,7 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf mptcp_data_lock(sk); DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk)); + mptcp_borrow_fwdmem(sk, skb); skb_set_owner_r(skb, sk); __skb_queue_tail(&sk->sk_receive_queue, skb); mptcp_sk(sk)->bytes_received += skb->len; diff --git a/net/mptcp/mib.c b/net/mptcp/mib.c index 171643815076..f23fda0c55a7 100644 --- a/net/mptcp/mib.c +++ b/net/mptcp/mib.c @@ -71,7 +71,6 @@ static const struct snmp_mib mptcp_snmp_list[] = { SNMP_MIB_ITEM("MPFastcloseRx", MPTCP_MIB_MPFASTCLOSERX), SNMP_MIB_ITEM("MPRstTx", MPTCP_MIB_MPRSTTX), SNMP_MIB_ITEM("MPRstRx", MPTCP_MIB_MPRSTRX), - SNMP_MIB_ITEM("RcvPruned", MPTCP_MIB_RCVPRUNED), SNMP_MIB_ITEM("SubflowStale", MPTCP_MIB_SUBFLOWSTALE), SNMP_MIB_ITEM("SubflowRecover", MPTCP_MIB_SUBFLOWRECOVER), SNMP_MIB_ITEM("SndWndShared", MPTCP_MIB_SNDWNDSHARED), diff --git a/net/mptcp/mib.h b/net/mptcp/mib.h index a1d3e9369fbb..812218b5ed2b 100644 --- a/net/mptcp/mib.h +++ b/net/mptcp/mib.h @@ -70,7 +70,6 @@ enum linux_mptcp_mib_field { MPTCP_MIB_MPFASTCLOSERX, /* Received a MP_FASTCLOSE */ MPTCP_MIB_MPRSTTX, /* Transmit a MP_RST */ MPTCP_MIB_MPRSTRX, /* Received a MP_RST */ - MPTCP_MIB_RCVPRUNED, /* Incoming packet dropped due to memory limit */ MPTCP_MIB_SUBFLOWSTALE, /* Subflows entered 'stale' status */ MPTCP_MIB_SUBFLOWRECOVER, /* Subflows returned to active status after being stale */ MPTCP_MIB_SNDWNDSHARED, /* Subflow snd wnd is overridden by msk's one */ diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 0dde73543da0..13a787605630 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -352,7 +352,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb) static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset, int copy_len) { - const struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); + struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); bool has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp; /* the skb map_seq accounts for the skb offset: @@ -377,11 +377,7 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb) struct mptcp_sock *msk = mptcp_sk(sk); struct sk_buff *tail; - /* try to fetch required memory from subflow */ - if (!sk_rmem_schedule(sk, skb, skb->truesize)) { - MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_RCVPRUNED); - goto drop; - } + mptcp_borrow_fwdmem(sk, skb); if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) { /* in sequence */ @@ -403,7 +399,6 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb) * will retransmit as needed, if needed. */ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA); -drop: mptcp_drop(sk, skb); return false; } @@ -704,7 +699,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk, size_t len = skb->len - offset; mptcp_init_skb(ssk, skb, offset, len); - skb_orphan(skb); + mptcp_subflow_lend_fwdmem(subflow, skb); ret = __mptcp_move_skb(sk, skb) || ret; seq += len; @@ -2454,6 +2449,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, { struct mptcp_sock *msk = mptcp_sk(sk); bool dispose_it, need_push = false; + int fwd_remaining; /* Do not pass RX data to the msk, even if the subflow socket is not * going to be freed (i.e. even for the first subflow on graceful @@ -2462,6 +2458,17 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk, lock_sock_nested(ssk, SINGLE_DEPTH_NESTING); subflow->closing = 1; + /* Borrow the fwd allocated page left-over; fwd memory for the subflow + * could be negative at this point, but will be reach zero soon - when + * the data allocated using such fragment will be freed. + */ + if (subflow->lent_mem_frag) { + fwd_remaining = PAGE_SIZE - subflow->lent_mem_frag; + sk_forward_alloc_add(sk, fwd_remaining); + sk_forward_alloc_add(ssk, -fwd_remaining); + subflow->lent_mem_frag = 0; + } + /* If the first subflow moved to a close state before accept, e.g. due * to an incoming reset or listener shutdown, the subflow socket is * already deleted by inet_child_forget() and the mptcp socket can't diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index feacb1feab47..df35dade0280 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -548,6 +548,7 @@ struct mptcp_subflow_context { bool scheduled; bool pm_listener; /* a listener managed by the kernel PM? */ bool fully_established; /* path validated */ + u32 lent_mem_frag; u32 remote_nonce; u64 thmac; u32 local_nonce; @@ -647,6 +648,33 @@ mptcp_send_active_reset_reason(struct sock *sk) tcp_send_active_reset(sk, GFP_ATOMIC, reason); } +/* Made the fwd mem carried by the given skb available to the msk, + * To be paired with a previous mptcp_subflow_lend_fwdmem() before freeing + * the skb or setting the skb ownership. + */ +static inline void mptcp_borrow_fwdmem(struct sock *sk, struct sk_buff *skb) +{ + struct sock *ssk = skb->sk; + + /* The subflow just lend the skb fwd memory, and we know that the skb + * is only accounted on the incoming subflow rcvbuf. + */ + DEBUG_NET_WARN_ON_ONCE(skb->destructor); + skb->sk = NULL; + sk_forward_alloc_add(sk, skb->truesize); + atomic_sub(skb->truesize, &ssk->sk_rmem_alloc); +} + +static inline void +mptcp_subflow_lend_fwdmem(struct mptcp_subflow_context *subflow, + struct sk_buff *skb) +{ + int frag = (subflow->lent_mem_frag + skb->truesize) & (PAGE_SIZE - 1); + + skb->destructor = NULL; + subflow->lent_mem_frag = frag; +} + static inline u64 mptcp_subflow_get_map_offset(const struct mptcp_subflow_context *subflow) { From bb37498a99e4e3a25a47265785979402dd79de30 Mon Sep 17 00:00:00 2001 From: Shardul Bankar Date: Sat, 30 May 2026 07:48:10 -0400 Subject: [PATCH 671/697] mptcp: do not drop partial packets [ Upstream commit 50c2d91c5dfa0e465826ec1f8dbad9cdc254bd85 ] When a packet arrives with map_seq < ack_seq < end_seq, the beginning of the packet has already been acknowledged but the end contains new data. Currently the entire packet is dropped as "old data," forcing the sender to retransmit. Instead, skip the already-acked bytes by adjusting the skb offset and enqueue only the new portion. Update bytes_received and ack_seq to reflect the new data consumed. A previous attempt at this fix has been sent by Paolo Abeni [1], but had issues [2]: it also added a zero-window check and changed rcv_wnd_sent initialization, which caused test regressions. This version addresses only the partial packet handling without modifying receive window accounting. Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/c9b426a4e163aa3c4fe8b80c79f1a610f47ae7d8.1763075056.git.pabeni@redhat.com [1] Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600 [2] Signed-off-by: Shardul Bankar [pabeni@redhat.com: update map] Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-1-701e96419f2f@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index 13a787605630..a47eb9b9feef 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -395,12 +395,26 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb) return false; } - /* old data, keep it simple and drop the whole pkt, sender - * will retransmit as needed, if needed. + /* Completely old data? */ + if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) { + MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA); + mptcp_drop(sk, skb); + return false; + } + + /* Partial packet: map_seq < ack_seq < end_seq. + * Skip the already-acked bytes and enqueue the new data. */ - MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA); - mptcp_drop(sk, skb); - return false; + copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq; + MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq; + MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq - + MPTCP_SKB_CB(skb)->map_seq; + msk->bytes_received += copy_len; + WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len); + + skb_set_owner_r(skb, sk); + __skb_queue_tail(&sk->sk_receive_queue, skb); + return true; } static void mptcp_stop_rtx_timer(struct sock *sk) From fe93e907b1af03cc229a80aa64a570a103d2b279 Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sun, 31 May 2026 20:38:49 -0400 Subject: [PATCH 672/697] arm64: tlb: Flush walk cache when unsharing PMD tables [ Upstream commit c2ff4764e03e7a8d758352f4aceb8fe1be6ac971 ] When huge_pmd_unshare() is called to unshare a PMD table, the tlb_unshare_pmd_ptdesc() function sets tlb->unshared_tables=true but the aarch64 tlb_flush() only checked tlb->freed_tables to determine whether to use TLBF_NONE (vae1is, invalidates walk cache) or TLBF_NOWALKCACHE (vale1is, leaf-only). This caused the stale PMD page table entry to remain in the walk cache after unshare, potentially leading to incorrect page table walks. Fix by including unshared_tables in the check, so that when unsharing tables, TLBF_NONE is used and the walk cache is properly invalidated. Here is the detailed distinction between vae1is and vale1is: | Instruction Combination | Actual Invalidation Scope | | ------------------------ | --------------------------------------------------| | `VAE1IS` + TTL=`0` | All entries at all levels (full invalidation) | | `VAE1IS` + TTL=`2` (L2) | Non-leaf at Level 0/1 + leaf at Level 2 | | `VALE1IS` + TTL=`0` | Leaf entries at all levels (non-leaf not cleared) | | `VALE1IS` + TTL=`2` (L2) | Leaf entry at Level 2 only | Signed-off-by: Zeng Heng Fixes: 8ce720d5bd91 ("mm/hugetlb: fix excessive IPI broadcasts when unsharing PMD tables using mmu_gather") Cc: Signed-off-by: Catalin Marinas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/tlb.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h index 8d762607285c..b3b863c919fe 100644 --- a/arch/arm64/include/asm/tlb.h +++ b/arch/arm64/include/asm/tlb.h @@ -53,7 +53,7 @@ static inline int tlb_get_level(struct mmu_gather *tlb) static inline void tlb_flush(struct mmu_gather *tlb) { struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0); - bool last_level = !tlb->freed_tables; + bool last_level = !(tlb->freed_tables || tlb->unshared_tables); unsigned long stride = tlb_get_unmap_size(tlb); int tlb_level = tlb_get_level(tlb); From 0d9b9d7dbef976ae7f855b6358f1d703014e96ea Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sat, 30 May 2026 12:52:02 -0400 Subject: [PATCH 673/697] octeontx2-pf: avoid double free of pool->stack on AQ init failure [ Upstream commit 9b244c242bec48b37e82b89787afd6a4c43457e1 ] otx2_pool_aq_init() frees pool->stack when mailbox sync or retry allocation fails, but leaves the pointer unchanged. Later, otx2_sq_aura_pool_init() unwinds the partial setup through otx2_aura_pool_free(), which frees pool->stack again. The CN20K-specific cn20k_pool_aq_init() implementation has the same bug in its corresponding error path. Set pool->stack to NULL immediately after the local free so the shared cleanup path does not free the same stack again while cleaning up partially initialized pool state. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1-rc3. Runtime validation was not performed because reproducing this path requires OcteonTX2/CN20K hardware. Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") Fixes: d322fbd17203 ("octeontx2-pf: Initialize cn20k specific aura and pool contexts") Cc: stable@vger.kernel.org Signed-off-by: Zilin Guan Signed-off-by: Dawei Feng Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260515151826.1005397-1-dawei.feng@seu.edu.cn Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 902d6abaa3ec..062bc899d955 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -1468,11 +1468,13 @@ int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id, err = otx2_sync_mbox_msg(&pfvf->mbox); if (err) { qmem_free(pfvf->dev, pool->stack); + pool->stack = NULL; return err; } aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); if (!aq) { qmem_free(pfvf->dev, pool->stack); + pool->stack = NULL; return -ENOMEM; } } From 82e742b9d2cc75721999e6e084155c855695edc7 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Sat, 30 May 2026 07:48:23 -0400 Subject: [PATCH 674/697] mptcp: cleanup fallback dummy mapping generation [ Upstream commit 2834f8edd74d5dda368087a654c0e52b141e9893 ] MPTCP currently access ack_seq outside the msk socket log scope to generate the dummy mapping for fallback socket. Soon we are going to introduce backlog usage and even for fallback socket the ack_seq value will be significantly off outside of the msk socket lock scope. Avoid relying on ack_seq for dummy mapping generation, using instead the subflow sequence number. Note that in case of disconnect() and (re)connect() we must ensure that any previous state is re-set. Signed-off-by: Paolo Abeni Reviewed-by: Geliang Tang Tested-by: Geliang Tang Reviewed-by: Mat Martineau Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20251121-net-next-mptcp-memcg-backlog-imp-v1-6-1f34b6c1e0b1@kernel.org Signed-off-by: Jakub Kicinski Stable-dep-of: 0981f90e1a05 ("mptcp: reset rcv wnd on disconnect") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 3 +++ net/mptcp/subflow.c | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index a47eb9b9feef..f8a743edd03a 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3333,6 +3333,9 @@ static int mptcp_disconnect(struct sock *sk, int flags) msk->rcvspace_init = 0; msk->fastclosing = 0; + /* for fallback's sake */ + WRITE_ONCE(msk->ack_seq, 0); + WRITE_ONCE(sk->sk_shutdown, 0); sk_error_report(sk); return 0; diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 3226fef7237e..42fd759c51df 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -491,6 +491,9 @@ static void subflow_set_remote_key(struct mptcp_sock *msk, mptcp_crypto_key_sha(subflow->remote_key, NULL, &subflow->iasn); subflow->iasn++; + /* for fallback's sake */ + subflow->map_seq = subflow->iasn; + WRITE_ONCE(msk->remote_key, subflow->remote_key); WRITE_ONCE(msk->ack_seq, subflow->iasn); WRITE_ONCE(msk->can_ack, true); @@ -1435,9 +1438,12 @@ static bool subflow_check_data_avail(struct sock *ssk) skb = skb_peek(&ssk->sk_receive_queue); subflow->map_valid = 1; - subflow->map_seq = READ_ONCE(msk->ack_seq); subflow->map_data_len = skb->len; subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq - subflow->ssn_offset; + subflow->map_seq = __mptcp_expand_seq(subflow->map_seq, + subflow->iasn + + TCP_SKB_CB(skb)->seq - + subflow->ssn_offset - 1); WRITE_ONCE(subflow->data_avail, true); return true; } From 8f72a2509163ae24a9aff1f269a9ff481ee273c8 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Sat, 30 May 2026 07:48:24 -0400 Subject: [PATCH 675/697] mptcp: reset rcv wnd on disconnect [ Upstream commit 0981f90e1a05773a4c29c6e720f5ea1e3c8f1876 ] If the MPTCP socket fallback to TCP before the MP handshake completion, the IASN remain 0, and the rcv_wnd_sent field is not explicitly initialized, just incremented over time with the data transfer. At disconnect time such value is not cleared. If the next connection falls back to TCP before the MP handshake completion, the data transfer will keep incrementing the receive window end sequence starting from the last value used in the previous connection: the announced window will be unrelated from the actual receiver buffer size and likely too big. Address the issue zeroing the field at disconnect time. Fixes: b29fcfb54cd7 ("mptcp: full disconnect implementation") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-4-701e96419f2f@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/mptcp/protocol.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index f8a743edd03a..32b2717b97ae 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -3335,6 +3335,7 @@ static int mptcp_disconnect(struct sock *sk, int flags) /* for fallback's sake */ WRITE_ONCE(msk->ack_seq, 0); + atomic64_set(&msk->rcv_wnd_sent, 0); WRITE_ONCE(sk->sk_shutdown, 0); sk_error_report(sk); From 7cb2daed350940bfc927e4ac894a4e4e507309e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 1 Jun 2026 07:02:03 -0400 Subject: [PATCH 676/697] cpufreq: intel_pstate: Add and use hybrid_get_cpu_type() [ Upstream commit 528dde6619677ac6dc26d9dda1e3c9014b4a08c8 ] Introduce a function for identifying the type of a given CPU in a hybrid system, called hybrid_get_cpu_type(), and use if for hybrid scaling factor determination in hwp_get_cpu_scaling(). Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1954386.tdWV9SEqCh@rafael.j.wysocki Stable-dep-of: 0e7c710478b3 ("cpufreq: intel_pstate: Use correct scaling factor on Raptor Lake-E") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index eb7fb25dfbef..6e1a793408de 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -909,6 +909,11 @@ static struct freq_attr *hwp_cpufreq_attrs[] = { [HWP_CPUFREQ_ATTR_COUNT] = NULL, }; +static u8 hybrid_get_cpu_type(unsigned int cpu) +{ + return cpu_data(cpu).topo.intel_type; +} + static bool no_cas __ro_after_init; static struct cpudata *hybrid_max_perf_cpu __read_mostly; @@ -2299,18 +2304,14 @@ static int knl_get_turbo_pstate(int cpu) static int hwp_get_cpu_scaling(int cpu) { if (hybrid_scaling_factor) { - struct cpuinfo_x86 *c = &cpu_data(cpu); - u8 cpu_type = c->topo.intel_type; - /* * Return the hybrid scaling factor for P-cores and use the * default core scaling for E-cores. */ - if (cpu_type == INTEL_CPU_TYPE_CORE) + if (hybrid_get_cpu_type(cpu) == INTEL_CPU_TYPE_CORE) return hybrid_scaling_factor; - if (cpu_type == INTEL_CPU_TYPE_ATOM) - return core_get_scaling(); + return core_get_scaling(); } /* Use core scaling on non-hybrid systems. */ From bb50838a2a0679642b10455335b8b4f43ab5b011 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 1 Jun 2026 07:02:04 -0400 Subject: [PATCH 677/697] cpufreq: intel_pstate: Use correct scaling factor on Raptor Lake-E [ Upstream commit 0e7c710478b3089cdfe8669347f77b163e836c4f ] Raptor Lake-E has the same processor ID as Raptor Lake-S, so there is an entry in intel_hybrid_scaling_factor[] for it. It does not contain E-cores though and hybrid_get_cpu_type() returns 0 for its P-cores, so they get the default "core" scaling factor. However, the original Raptor Lake scaling factor for P-cores still needs to be used for mapping the HWP performance levels of the P-cores in Raptor Lake-E to frequency, as though they were part of a real hybrid system. To address this, update hwp_get_cpu_scaling() to return hybrid_scaling_factor, which is the P-core scaling factor retrieved from intel_hybrid_scaling_factor[], for all CPUs that are not enumerated as E-cores. Fixes: 9b18d536b124 ("cpufreq: intel_pstate: Use CPPC to get scaling factors") Link: https://lore.kernel.org/all/20260511235328.2018458-1-srinivas.pandruvada@linux.intel.com/ Reported-by: Henry Tseng Closes: https://lore.kernel.org/linux-pm/20260508063032.3248602-1-henrytseng@qnap.com/ Signed-off-by: Rafael J. Wysocki Cc: All applicable Link: https://patch.msgid.link/4523296.ejJDZkT8p0@rafael.j.wysocki Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/cpufreq/intel_pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6e1a793408de..7fb6412b5f19 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2308,7 +2308,7 @@ static int hwp_get_cpu_scaling(int cpu) * Return the hybrid scaling factor for P-cores and use the * default core scaling for E-cores. */ - if (hybrid_get_cpu_type(cpu) == INTEL_CPU_TYPE_CORE) + if (hybrid_get_cpu_type(cpu) != INTEL_CPU_TYPE_ATOM) return hybrid_scaling_factor; return core_get_scaling(); From 9327252e04626d4bb02ca8c0c108fbe8eabf0c5a Mon Sep 17 00:00:00 2001 From: Shaomin Chen Date: Fri, 5 Jun 2026 08:59:53 -0400 Subject: [PATCH 678/697] xfrm: iptfs: reset runtime state when cloning SAs [ Upstream commit 7f83d174073234839aea176f265e517e0d50a1d2 ] iptfs_clone_state() clones the IPTFS mode data with kmemdup(). This copies runtime objects which must not be shared with the original SA, including the embedded sk_buff_head, hrtimers, spinlock, and in-flight reassembly/reorder state. If xfrm_state_migrate() fails after clone_state() but before the later init_state() call has reinitialized those fields, the cloned state can be destroyed by xfrm_state_gc_task() with list and timer state copied from the original SA. With queued packets this lets the clone splice and free skbs owned by the original IPTFS queue, leading to use-after-free and double-free reports in iptfs_destroy_state() and skb release paths. Reinitialize the clone's runtime state before publishing it through x->mode_data. Because clone_state() now publishes a destroyable mode_data object before init_state(), take the mode callback module reference there. Avoid taking it again from __iptfs_init_state() for the same object. Fixes: 0e4fbf013fa5 ("xfrm: iptfs: add user packet (tunnel ingress) handling") Cc: stable@vger.kernel.org Signed-off-by: Shaomin Chen Signed-off-by: Steffen Klassert Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- net/xfrm/xfrm_iptfs.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/net/xfrm/xfrm_iptfs.c b/net/xfrm/xfrm_iptfs.c index 7cd97c1dcd11..e11e4f7411fd 100644 --- a/net/xfrm/xfrm_iptfs.c +++ b/net/xfrm/xfrm_iptfs.c @@ -2650,7 +2650,8 @@ static void __iptfs_init_state(struct xfrm_state *x, x->props.enc_hdr_len = sizeof(struct ip_iptfs_hdr); /* Always keep a module reference when x->mode_data is set */ - __module_get(x->mode_cbs->owner); + if (x->mode_data != xtfs) + __module_get(x->mode_cbs->owner); x->mode_data = xtfs; xtfs->x = x; @@ -2658,22 +2659,40 @@ static void __iptfs_init_state(struct xfrm_state *x, static int iptfs_clone_state(struct xfrm_state *x, struct xfrm_state *orig) { + struct skb_wseq *w_saved = NULL; struct xfrm_iptfs_data *xtfs; xtfs = kmemdup(orig->mode_data, sizeof(*xtfs), GFP_KERNEL); if (!xtfs) return -ENOMEM; - xtfs->ra_newskb = NULL; if (xtfs->cfg.reorder_win_size) { - xtfs->w_saved = kcalloc(xtfs->cfg.reorder_win_size, - sizeof(*xtfs->w_saved), GFP_KERNEL); - if (!xtfs->w_saved) { + w_saved = kcalloc(xtfs->cfg.reorder_win_size, + sizeof(*w_saved), GFP_KERNEL); + if (!w_saved) { kfree_sensitive(xtfs); return -ENOMEM; } } + xtfs->w_saved = w_saved; + + __skb_queue_head_init(&xtfs->queue); + xtfs->queue_size = 0; + hrtimer_setup(&xtfs->iptfs_timer, iptfs_delay_timer, CLOCK_MONOTONIC, + IPTFS_HRTIMER_MODE); + + spin_lock_init(&xtfs->drop_lock); + hrtimer_setup(&xtfs->drop_timer, iptfs_drop_timer, CLOCK_MONOTONIC, + IPTFS_HRTIMER_MODE); + xtfs->w_seq_set = false; + xtfs->w_wantseq = 0; + xtfs->w_savedlen = 0; + xtfs->ra_newskb = NULL; + xtfs->ra_wantseq = 0; + xtfs->ra_runtlen = 0; + + __module_get(x->mode_cbs->owner); x->mode_data = xtfs; xtfs->x = x; From c73c62a4bd52d9e3b2b0558f934dc136e8f9ca5e Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Fri, 5 Jun 2026 09:39:59 -0400 Subject: [PATCH 679/697] usb: dwc3: xilinx: fix error handling in zynqmp init error paths [ Upstream commit c1a0ecbf32c4b397353204e2ec94c5bb9f3300ed ] Fix error handling and resource cleanup i.e remove invalid phy_exit() after failed phy_init(), route failures through proper cleanup paths and return 0 explicitly on success. Fixes: 84770f028fab ("usb: dwc3: Add driver for Xilinx platforms") Cc: stable@vger.kernel.org Acked-by: Thinh Nguyen Signed-off-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260519115529.2980421-1-radhey.shyam.pandey@amd.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-xilinx.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 1e28d6f50ed0..c2b8573c2567 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -190,15 +190,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } ret = phy_init(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); + if (ret < 0) goto err; - } ret = reset_control_deassert(apbrst); if (ret < 0) { dev_err(dev, "Failed to release APB reset\n"); - goto err; + goto err_phy_exit; } /* Set PIPE Power Present signal in FPD Power Present Register*/ @@ -210,27 +208,25 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) ret = reset_control_deassert(crst); if (ret < 0) { dev_err(dev, "Failed to release core reset\n"); - goto err; + goto err_phy_exit; } ret = reset_control_deassert(hibrst); if (ret < 0) { dev_err(dev, "Failed to release hibernation reset\n"); - goto err; + goto err_phy_exit; } ret = phy_power_on(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); - goto err; - } + if (ret < 0) + goto err_phy_exit; skip_usb3_phy: /* ulpi reset via gpio-modepin or gpio-framework driver */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) { - return dev_err_probe(dev, PTR_ERR(reset_gpio), - "Failed to request reset GPIO\n"); + ret = PTR_ERR(reset_gpio); + goto err_phy_power_off; } if (reset_gpio) { @@ -240,6 +236,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG); + + return 0; + +err_phy_power_off: + phy_power_off(priv_data->usb3_phy); +err_phy_exit: + phy_exit(priv_data->usb3_phy); err: return ret; } From 284105c40fc31fff90cdab8a0377aaeb92f87f0e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 10:36:36 +0200 Subject: [PATCH 680/697] USB: serial: cypress_m8: fix memory corruption with small endpoint commit e1a9d791fd66ab2431b9e6f6f835823809869047 upstream. Make sure that the interrupt-out endpoint max packet size is at least eight bytes to avoid user-controlled slab corruption or NULL-pointer dereference should a malicious device report a smaller size. Fixes: 3416eaa1f8f8 ("USB: cypress_m8: Packet format is separate from characteristic size") Cc: stable@vger.kernel.org # 2.6.26 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold [ johan: adjust context for 6.18 ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cypress_m8.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index eb47f35aab0c..905f6a560e04 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * The buffer must be large enough for the one or two-byte header (and + * following data), but assume anything smaller than eight bytes is + * broken. + */ + if (port->interrupt_out_size < 8) + return -EINVAL; + priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL); if (!priv) return -ENOMEM; From 062dcc0b324afd03b1406f157190804f105718bb Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 14:07:58 +0200 Subject: [PATCH 681/697] USB: serial: digi_acceleport: fix memory corruption with small endpoints commit cb3560e8eab1dfa1cac1ed52631adf8ec6ff2cd5 upstream. Add the missing bulk-out buffer size sanity checks to avoid out-of-bounds memory accesses or slab corruption should a malicious device report smaller buffers than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/digi_acceleport.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index a06485965412..a876d6629b65 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) static int digi_startup(struct usb_serial *serial) { struct digi_serial *serial_priv; + int oob_port_num; int ret; + int i; + + /* + * The port bulk-out buffers must be large enough for header and + * buffered data. + */ + for (i = 0; i < serial->type->num_ports; i++) { + if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2) + return -EINVAL; + } + + /* + * The OOB port bulk-out buffer must be large enough for the two + * commands in digi_set_modem_signals(). + */ + oob_port_num = serial->type->num_ports; + if (serial->port[oob_port_num]->bulk_out_size < 8) + return -EINVAL; serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL); if (!serial_priv) return -ENOMEM; spin_lock_init(&serial_priv->ds_serial_lock); - serial_priv->ds_oob_port_num = serial->type->num_ports; - serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num]; + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; ret = digi_port_init(serial_priv->ds_oob_port, serial_priv->ds_oob_port_num); From d8fdf33d6fcfb90cbec26299baf2352c84b2d768 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 4 Jun 2026 14:11:33 +0200 Subject: [PATCH 682/697] USB: serial: mct_u232: fix memory corruption with small endpoint commit 915b36d701950503c4ea0f6e314b10868e59fce3 upstream. The driver overrides the maximum transfer size for a specific device which only accepts 16 byte packets for its 32 byte bulk-out endpoint. Make sure to never increase the maximum transfer size to prevent slab corruption should a malicious device report a smaller endpoint max packet size than expected. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mct_u232.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index d225d7c1455f..33d4bbc461be 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -378,6 +378,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv; + u16 pid; /* check first to simplify error handling */ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { @@ -385,6 +386,16 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * Compensate for a hardware bug: although the Sitecom U232-P25 + * device reports a maximum output packet size of 32 bytes, + * it seems to be able to accept only 16 bytes (and that's what + * SniffUSB says too...) + */ + pid = le16_to_cpu(serial->dev->descriptor.idProduct); + if (pid == MCT_U232_SITECOM_PID) + port->bulk_out_size = min(16, port->bulk_out_size); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -410,7 +421,6 @@ static void mct_u232_port_remove(struct usb_serial_port *port) static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int retval = 0; unsigned int control_state; @@ -418,15 +428,6 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char last_lcr; unsigned char last_msr; - /* Compensate for a hardware bug: although the Sitecom U232-P25 - * device reports a maximum output packet size of 32 bytes, - * it seems to be able to accept only 16 bytes (and that's what - * SniffUSB says too...) - */ - if (le16_to_cpu(serial->dev->descriptor.idProduct) - == MCT_U232_SITECOM_PID) - port->bulk_out_size = 16; - /* Do a defined restart: the normal serial device seems to * always turn on DTR and RTS here, so do the same. I'm not * sure if this is really necessary. But it should not harm From 192516d727741be788a4483c16823be25a0013ba Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 1 Jun 2026 08:49:48 -0400 Subject: [PATCH 683/697] hwmon: (pmbus) Add support for guarded PMBus lock [ Upstream commit 1814f4d3ff358277a5b6957e7f133c2812dc80ec ] Add support for guard(pmbus_lock)() and scoped_guard(pmbus_lock)() to be able to simplify the PMBus code. Also introduce pmbus_lock() as pre-requisite for supporting guard(). Reviewed-by: Sanman Pradhan Signed-off-by: Guenter Roeck Stable-dep-of: 4e4af55aaca7 ("hwmon: (pmbus/adm1266) serialize sequencer_state debugfs read with pmbus_lock") Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/pmbus.h | 5 +++++ drivers/hwmon/pmbus/pmbus_core.c | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index d2e9bfb5320f..e499cdae9442 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -10,6 +10,7 @@ #define PMBUS_H #include +#include #include /* @@ -563,7 +564,11 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode); int pmbus_lock_interruptible(struct i2c_client *client); +void pmbus_lock(struct i2c_client *client); void pmbus_unlock(struct i2c_client *client); + +DEFINE_GUARD(pmbus_lock, struct i2c_client *, pmbus_lock(_T), pmbus_unlock(_T)) + int pmbus_update_fan(struct i2c_client *client, int page, int id, u8 config, u8 mask, u16 command); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 572be3ebc03d..7150f12d2630 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3871,6 +3871,14 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) } EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS"); +void pmbus_lock(struct i2c_client *client) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); +} +EXPORT_SYMBOL_NS_GPL(pmbus_lock, "PMBUS"); + int pmbus_lock_interruptible(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); From 6b94f9f5fe2889c1dab94d9562d08495989252c0 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 1 Jun 2026 08:49:49 -0400 Subject: [PATCH 684/697] hwmon: (pmbus/adm1266) serialize sequencer_state debugfs read with pmbus_lock [ Upstream commit 4e4af55aaca7f6d7673d5f9889ad0529db86a048 ] adm1266_state_read() backs the sequencer_state debugfs entry and issues an i2c_smbus_read_word_data(client, ADM1266_READ_STATE) against the device without taking pmbus_lock. pmbus_core holds pmbus_lock around its own multi-transaction sequences (notably the "set PAGE, then read paged register" pattern used by hwmon attributes), so an unlocked debugfs reader can land between a PAGE write and the subsequent paged read in another thread. READ_STATE itself is not paged, so it cannot corrupt PAGE in flight, but the same defensive serialisation that applies to the GPIO accessors applies here: any direct device access from outside pmbus_core should be ordered with respect to pmbus_core's own. Take pmbus_lock at the top of adm1266_state_read() via the scope-based guard(). Fixes: ed1ff457e187 ("hwmon: (pmbus/adm1266) add debugfs for states") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-8-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 9631a64cb1eb..9e05fe7911f8 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -328,6 +328,7 @@ static int adm1266_state_read(struct seq_file *s, void *pdata) struct i2c_client *client = to_i2c_client(dev); int ret; + guard(pmbus_lock)(client); ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE); if (ret < 0) return ret; From 56857385f313efab5bba1bf821223d984b1be671 Mon Sep 17 00:00:00 2001 From: Abdurrahman Hussain Date: Mon, 1 Jun 2026 09:53:37 -0400 Subject: [PATCH 685/697] hwmon: (pmbus/adm1266) serialize GPIO PMBus accesses with pmbus_lock [ Upstream commit bab8c6fb5af8df7e753d196c1262cb78e92ca872 ] adm1266_gpio_get(), adm1266_gpio_get_multiple(), and adm1266_gpio_dbg_show() all issue PMBus reads against the device but none of them take pmbus_lock. The pmbus_core framework holds pmbus_lock around its own multi-transaction sequences (notably the "set PAGE, then read paged register" pattern used by hwmon attributes), so an unlocked GPIO accessor can land between a PAGE write and the subsequent paged read in another thread and corrupt either side's view of the device state machine. Take pmbus_lock at the top of each of the three accessors via the scope-based guard(). The lock is uncontended in the common case and adds only a single mutex round-trip per call. Fixes: d98dfad35c38 ("hwmon: (pmbus/adm1266) Add support for GPIOs") Cc: stable@vger.kernel.org Signed-off-by: Abdurrahman Hussain Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20260518-adm1266-gpio-fixes-v3-6-e425e4f88139@nexthop.ai Signed-off-by: Guenter Roeck Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/pmbus/adm1266.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 9e05fe7911f8..58917ef0f304 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -173,6 +173,8 @@ static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset) else pmbus_cmd = ADM1266_PDIO_STATUS; + guard(pmbus_lock)(data->client); + ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf); if (ret < 0) return ret; @@ -195,6 +197,8 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask unsigned int gpio_nr; int ret; + guard(pmbus_lock)(data->client); + ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf); if (ret < 0) return ret; @@ -236,6 +240,8 @@ static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) int ret; int i; + guard(pmbus_lock)(data->client); + for (i = 0; i < ADM1266_GPIO_NR; i++) { write_cmd = adm1266_gpio_mapping[i][1]; ret = adm1266_pmbus_block_xfer(data, ADM1266_GPIO_CONFIG, 1, &write_cmd, read_buf); From 972ea882d4bf7077f6c0bafd9665928ebfc29245 Mon Sep 17 00:00:00 2001 From: Qing Wang Date: Mon, 1 Jun 2026 07:05:09 -0400 Subject: [PATCH 686/697] mm/slub: hold cpus_read_lock around flush_rcu_sheaves_on_cache() [ Upstream commit 67ea9d353d0ba12bdbc9183ff568dead9e949b80 ] flush_rcu_sheaves_on_cache() calls queue_work_on() in a for_each_online_cpu() loop, which requires the cpu to stay online. But cpus_read_lock() is not held in kvfree_rcu_barrier_on_cache() and the set of "online cpus" is subject to change. There are two paths that call flush_rcu_sheaves_on_cache(): // has cpus_read_lock() flush_all_rcu_sheaves() -> flush_rcu_sheaves_on_cache() // no cpus_read_lock() kvfree_rcu_barrier_on_cache() -> flush_rcu_sheaves_on_cache() Fix this by holding cpus_read_lock() in kvfree_rcu_barrier_on_cache(). Why not move cpus_read_lock() from flush_all_rcu_sheaves() into flush_rcu_sheaves_on_cache()? The reason is it would introduce a new lock order (slab_mutex -> cpu_hotplug_lock). The reverse order (cpu_hotplug_lock -> slab_mutex) is established by - cpuhp_setup_state_nocalls(..., slub_cpu_setup, ...) - kmem_cache_destroy() The two orders together would form an AB-BA deadlock. Finally, add lockdep_assert_cpus_held() in flush_rcu_sheaves_on_cache() to catch the same problem in the future. Fixes: 0f35040de593 ("mm/slab: introduce kvfree_rcu_barrier_on_cache() for cache destruction") Cc: Signed-off-by: Qing Wang Link: https://patch.msgid.link/20260512035035.762317-1-wangqing7171@gmail.com Signed-off-by: Vlastimil Babka (SUSE) Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- mm/slab_common.c | 2 ++ mm/slub.c | 1 + 2 files changed, 3 insertions(+) diff --git a/mm/slab_common.c b/mm/slab_common.c index 0f58265ca200..04583044a2bf 100644 --- a/mm/slab_common.c +++ b/mm/slab_common.c @@ -2135,7 +2135,9 @@ EXPORT_SYMBOL_GPL(kvfree_rcu_barrier); void kvfree_rcu_barrier_on_cache(struct kmem_cache *s) { if (s->cpu_sheaves) { + cpus_read_lock(); flush_rcu_sheaves_on_cache(s); + cpus_read_unlock(); rcu_barrier(); } diff --git a/mm/slub.c b/mm/slub.c index a89df6ddcc58..5fdec3b83706 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -4203,6 +4203,7 @@ void flush_rcu_sheaves_on_cache(struct kmem_cache *s) struct slub_flush_work *sfw; unsigned int cpu; + lockdep_assert_cpus_held(); mutex_lock(&flush_lock); for_each_online_cpu(cpu) { From 58b2c0f096b36a93e1975b6fa7f8d629e6fd9425 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 28 Apr 2026 15:41:01 +0200 Subject: [PATCH 687/697] net: phy: micrel: fix LAN8814 QSGMII soft reset [ Upstream commit e027c218c482c6a0ae1948129ccda3b0a2033368 ] LAN8814 QSGMII soft reset was moved into the probe function to avoid triggering it for each of 4 PHY-s in the package. However, that broke QSGMII link between the MAC and PHY on most LAN8814 PHY-s, specificaly for us on the Microchip LAN969x switch. Reading the QSGMII status registers it was visible that lanes were only partially synced. It looks like the reset timing is crucial, so lets move the reset back into the .config_init function but guard it with phy_package_init_once() to avoid it being triggered on each of 4 PHY-s in the package. Change the probe function to use phy_package_probe_once() for coma and PtP setup. Fixes: 96a9178a29a6 ("net: phy: micrel: lan8814 fix reset of the QSGMII interface") Signed-off-by: Robert Marko Link: https://patch.msgid.link/20260428134138.1741253-1-robert.marko@sartura.hr Signed-off-by: Jakub Kicinski Signed-off-by: Sasha Levin --- drivers/net/phy/micrel.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index bc19880107ae..e6f00aa9a990 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -4389,6 +4389,13 @@ static int lan8814_config_init(struct phy_device *phydev) { struct kszphy_priv *lan8814 = phydev->priv; + if (phy_package_init_once(phydev)) + /* Reset the PHY */ + lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, + LAN8814_QSGMII_SOFT_RESET, + LAN8814_QSGMII_SOFT_RESET_BIT, + LAN8814_QSGMII_SOFT_RESET_BIT); + /* Disable ANEG with QSGMII PCS Host side */ lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS, LAN8814_QSGMII_PCS1G_ANEG_CONFIG, @@ -4473,13 +4480,7 @@ static int lan8814_probe(struct phy_device *phydev) devm_phy_package_join(&phydev->mdio.dev, phydev, addr, sizeof(struct lan8814_shared_priv)); - if (phy_package_init_once(phydev)) { - /* Reset the PHY */ - lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, - LAN8814_QSGMII_SOFT_RESET, - LAN8814_QSGMII_SOFT_RESET_BIT, - LAN8814_QSGMII_SOFT_RESET_BIT); - + if (phy_package_probe_once(phydev)) { err = lan8814_release_coma_mode(phydev); if (err) return err; From 5372f6f10b0a85bb7bbbfd30197f31aa4a21488b Mon Sep 17 00:00:00 2001 From: Wei-Cheng Chen Date: Thu, 4 Jun 2026 20:19:07 +0800 Subject: [PATCH 688/697] xhci: tegra: Fix ghost USB device on dual-role port unplug [ Upstream commit 5a4c828b8b29b47534814ade26d9aee09d5101fc ] When a USB device is unplugged from the dual-role port, the device-mode path in tegra_xhci_id_work() explicitly clears both SS and HS port power via direct hub_control ClearPortFeature(POWER) calls. This preempts the xHCI controller's normal disconnect processing -- PORT_CSC is never generated, the USB core never sees the disconnect, and the device remains in its internal tree as a ghost visible in lsusb. Add an otg_set_port_power flag to control whether the dual-role switch path performs explicit port power management. SoCs that need it (Tegra124 / Tegra210 / Tegra186) set the flag; later SoCs (Tegra194 and beyond) rely on the PHY mode change to handle disconnect naturally and skip all port power calls. Within the port power path, otg_reset_sspi additionally gates the SSPI reset sequence on host-mode entry for SoCs that require it. Flags set per SoC: Tegra124, Tegra186 -> otg_set_port_power Tegra210 -> otg_set_port_power, otg_reset_sspi Tegra194 and later -> (none) [ Backport to 6.18.y: keep the host-mode snapshot in the existing tegra->lock section, retain pm_runtime_mark_last_busy() in the host port-power path, preserve str_on_off(), and resolve context around the SoC ops/Tegra234 entries. ] Fixes: f836e7843036 ("usb: xhci-tegra: Add OTG support") Cc: stable@vger.kernel.org Signed-off-by: Wei-Cheng Chen Link: https://patch.msgid.link/20260505112630.217704-1-weichengc@nvidia.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: Sasha Levin --- drivers/usb/host/xhci-tegra.c | 79 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 83b1766ff152..b0dcdede1fc8 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -247,6 +247,7 @@ struct tegra_xusb_soc { bool has_ipfs; bool lpm_support; bool otg_reset_sspi; + bool otg_set_port_power; bool has_bar2; }; @@ -1352,14 +1353,17 @@ static void tegra_xhci_id_work(struct work_struct *work) struct tegra_xusb_mbox_msg msg; struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", tegra->otg_usb2_port); + bool host_mode; u32 status; int ret; - dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); - mutex_lock(&tegra->lock); - if (tegra->host_mode) + host_mode = tegra->host_mode; + + dev_dbg(tegra->dev, "host mode %s\n", str_on_off(host_mode)); + + if (host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); @@ -1370,42 +1374,44 @@ static void tegra_xhci_id_work(struct work_struct *work) tegra->otg_usb2_port); pm_runtime_get_sync(tegra->dev); - if (tegra->host_mode) { - /* switch to host mode */ - if (tegra->otg_usb3_port >= 0) { - if (tegra->soc->otg_reset_sspi) { - /* set PP=0 */ - tegra_xhci_hc_driver.hub_control( - xhci->shared_hcd, GetPortStatus, - 0, tegra->otg_usb3_port+1, - (char *) &status, sizeof(status)); - if (status & USB_SS_PORT_STAT_POWER) - tegra_xhci_set_port_power(tegra, false, - false); - - /* reset OTG port SSPI */ - msg.cmd = MBOX_CMD_RESET_SSPI; - msg.data = tegra->otg_usb3_port+1; - - ret = tegra_xusb_mbox_send(tegra, &msg); - if (ret < 0) { - dev_info(tegra->dev, - "failed to RESET_SSPI %d\n", - ret); + if (tegra->soc->otg_set_port_power) { + if (host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); + + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; + + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } } - } - tegra_xhci_set_port_power(tegra, false, true); - } + tegra_xhci_set_port_power(tegra, false, true); + } - tegra_xhci_set_port_power(tegra, true, true); - pm_runtime_mark_last_busy(tegra->dev); + tegra_xhci_set_port_power(tegra, true, true); + pm_runtime_mark_last_busy(tegra->dev); - } else { - if (tegra->otg_usb3_port >= 0) - tegra_xhci_set_port_power(tegra, false, false); + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); - tegra_xhci_set_port_power(tegra, true, false); + tegra_xhci_set_port_power(tegra, true, false); + } } pm_runtime_put_autosuspend(tegra->dev); } @@ -2558,6 +2564,7 @@ static const struct tegra_xusb_soc tegra124_soc = { .scale_ss_clock = true, .has_ipfs = true, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2596,6 +2603,7 @@ static const struct tegra_xusb_soc tegra210_soc = { .scale_ss_clock = false, .has_ipfs = true, .otg_reset_sspi = true, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2639,6 +2647,7 @@ static const struct tegra_xusb_soc tegra186_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2672,6 +2681,7 @@ static const struct tegra_xusb_soc tegra194_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra124_ops, .mbox = { .cmd = 0x68, @@ -2705,6 +2715,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra234_ops, .mbox = { .cmd = XUSB_BAR2_ARU_MBOX_CMD, From 00869f2320dc55f49e36f9677a6ce5c530d67534 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Fri, 27 Mar 2026 17:00:40 -0500 Subject: [PATCH 689/697] mailbox: Fix NULL message support in mbox_send_message() commit c58e9456e30c7098cbcd9f04571992be8a2e4e63 upstream. The active_req field serves double duty as both the "is a TX in flight" flag (NULL means idle) and the storage for the in-flight message pointer. When a client sends NULL via mbox_send_message(), active_req is set to NULL, which the framework misinterprets as "no active request". This breaks the TX state machine by: - tx_tick() short-circuits on (!mssg), skipping the tx_done callback and the tx_complete completion - txdone_hrtimer() skips the channel entirely since active_req is NULL, so poll-based TX-done detection never fires. Fix this by introducing a MBOX_NO_MSG sentinel value that means "no active request," freeing NULL to be valid message data. The sentinel is defined in the subsystem-internal mailbox.h so that controller drivers within drivers/mailbox/ can reference it, but it is not exposed to clients outside the subsystem. Fifteen in-tree callers send NULL (doorbell-style IPCs on Qualcomm, Tegra, TI, Xilinx, i.MX, SCMI, and PCC platforms). All were audited for regression: - Most already work around the bug via knows_txdone=true with a manual mbox_client_txdone() call, making the framework's tracking irrelevant. These are unaffected. - Poll-based callers (Xilinx zynqmp/r5) are strictly better off: the poll timer now correctly detects NULL-active channels instead of silently skipping them. - irq-qcom-mpm.c was a pre-existing bug -- the only Qualcomm caller that omitted the knows_txdone + mbox_client_txdone() pattern. Fixed in a companion commit ("irqchip/qcom-mpm: Fix missing mailbox TX done acknowledgment"). - No caller sets both a tx_done callback and sends NULL, nor combines tx_block=true with NULL sends, so the newly reachable callback/completion paths are never exercised. Also update tegra-hsp's flush callback, which directly inspects active_req to wait for the channel to drain: the old "!= NULL" check becomes "!= MBOX_NO_MSG", otherwise flush spins until timeout since the sentinel is non-NULL. The only tradeoff is that 'MBOX_NO_MSG' can not be used as a message by clients. Reported-by: Joonwon Kang Reviewed-by: Douglas Anderson Signed-off-by: Jassi Brar Signed-off-by: Joonwon Kang Signed-off-by: Greg Kroah-Hartman --- drivers/mailbox/mailbox.c | 15 ++++++++------- drivers/mailbox/tegra-hsp.c | 2 +- include/linux/mailbox_controller.h | 3 +++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index b77162db509f..60866e8417c9 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -52,7 +52,7 @@ static void msg_submit(struct mbox_chan *chan) int err = -EBUSY; scoped_guard(spinlock_irqsave, &chan->lock) { - if (!chan->msg_count || chan->active_req) + if (!chan->msg_count || chan->active_req != MBOX_NO_MSG) break; count = chan->msg_count; @@ -87,13 +87,13 @@ static void tx_tick(struct mbox_chan *chan, int r) scoped_guard(spinlock_irqsave, &chan->lock) { mssg = chan->active_req; - chan->active_req = NULL; + chan->active_req = MBOX_NO_MSG; } /* Submit next message */ msg_submit(chan); - if (!mssg) + if (mssg == MBOX_NO_MSG) return; /* Notify the client */ @@ -114,7 +114,7 @@ static enum hrtimer_restart txdone_hrtimer(struct hrtimer *hrtimer) for (i = 0; i < mbox->num_chans; i++) { struct mbox_chan *chan = &mbox->chans[i]; - if (chan->active_req && chan->cl) { + if (chan->active_req != MBOX_NO_MSG && chan->cl) { txdone = chan->mbox->ops->last_tx_done(chan); if (txdone) tx_tick(chan, 0); @@ -246,7 +246,7 @@ int mbox_send_message(struct mbox_chan *chan, void *mssg) { int t; - if (!chan || !chan->cl) + if (!chan || !chan->cl || mssg == MBOX_NO_MSG) return -EINVAL; t = add_to_rbuf(chan, mssg); @@ -319,7 +319,7 @@ static int __mbox_bind_client(struct mbox_chan *chan, struct mbox_client *cl) scoped_guard(spinlock_irqsave, &chan->lock) { chan->msg_free = 0; chan->msg_count = 0; - chan->active_req = NULL; + chan->active_req = MBOX_NO_MSG; chan->cl = cl; init_completion(&chan->tx_complete); @@ -477,7 +477,7 @@ void mbox_free_channel(struct mbox_chan *chan) /* The queued TX requests are simply aborted, no callbacks are made */ scoped_guard(spinlock_irqsave, &chan->lock) { chan->cl = NULL; - chan->active_req = NULL; + chan->active_req = MBOX_NO_MSG; if (chan->txdone_method == TXDONE_BY_ACK) chan->txdone_method = TXDONE_BY_POLL; } @@ -531,6 +531,7 @@ int mbox_controller_register(struct mbox_controller *mbox) chan->cl = NULL; chan->mbox = mbox; + chan->active_req = MBOX_NO_MSG; chan->txdone_method = txdone; spin_lock_init(&chan->lock); } diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c index ed9a0bb2bcd8..7991e8dba579 100644 --- a/drivers/mailbox/tegra-hsp.c +++ b/drivers/mailbox/tegra-hsp.c @@ -497,7 +497,7 @@ static int tegra_hsp_mailbox_flush(struct mbox_chan *chan, mbox_chan_txdone(chan, 0); /* Wait until channel is empty */ - if (chan->active_req != NULL) + if (chan->active_req != MBOX_NO_MSG) continue; return 0; diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index 80a427c7ca29..1db0069c27c5 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -11,6 +11,9 @@ struct mbox_chan; +/* Sentinel value distinguishing "no active request" from "NULL message data" */ +#define MBOX_NO_MSG ((void *)-1) + /** * struct mbox_chan_ops - methods to control mailbox channels * @send_data: The API asks the MBOX controller driver, in atomic From 21bfa15a89d888d37e4881d64fc473cb85f4697a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jouni=20H=C3=B6gander?= Date: Fri, 5 Jun 2026 22:18:39 -0400 Subject: [PATCH 690/697] drm/i915/psr: Use DC_OFF wake reference to block DC6 on vblank enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ Upstream commit 3549a9649dc7c5fc586ab12f675279283cdcb2a7 ] We are observing following warnings: *ERROR* power well DC_off state mismatch (refcount 0/enabled 1) gen9_dc_off_power_well_enabled is considering target state DC_STATE_DISABLE as DC_OFF power well being enabled. Fix this by using wakeref for the purpose. To achieve this we need to modify notification code as well. Currently it is possible that PSR gets notified vblank enable/disable twice on same status. This is currently not a problem as it is just triggering call to intel_display_power_set_target_dc_state with same target state as a parameter. When using wakeref this becomes a problem due to reference counting. Fix this storing vbank status on last notification and use that to ensure there are no more than one notification with same vblank status. v2: ensure there is no subsequent notifications with same status Fixes: aa451abcffb5 ("drm/i915/display: Prevent DC6 while vblank is enabled for Panel Replay") Cc: # v6.13+ Signed-off-by: Jouni Högander Reviewed-by: Michał Grzelak Link: https://patch.msgid.link/20260520104944.239797-2-jouni.hogander@intel.com (cherry picked from commit 35485ac56d878192a3829a58cb26503125ec7104) Signed-off-by: Tvrtko Ursulin Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- .../gpu/drm/i915/display/intel_display_core.h | 1 + .../gpu/drm/i915/display/intel_display_irq.c | 8 +++++-- .../drm/i915/display/intel_display_types.h | 2 ++ drivers/gpu/drm/i915/display/intel_psr.c | 24 +++++++------------ 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 8c226406c5cd..800a30b8bcd8 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -472,6 +472,7 @@ struct intel_display { u8 vblank_enabled; int vblank_enable_count; + bool vblank_status_last_notified; struct work_struct vblank_notify_work; diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 123e054affbe..21b2a193bb21 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1707,8 +1707,12 @@ static void intel_display_vblank_notify_work(struct work_struct *work) struct intel_display *display = container_of(work, typeof(*display), irq.vblank_notify_work); int vblank_enable_count = READ_ONCE(display->irq.vblank_enable_count); + bool vblank_status = !!vblank_enable_count; - intel_psr_notify_vblank_enable_disable(display, vblank_enable_count); + if (display->irq.vblank_status_last_notified != vblank_status) { + intel_psr_notify_vblank_enable_disable(display, vblank_status); + display->irq.vblank_status_last_notified = vblank_status; + } } int bdw_enable_vblank(struct drm_crtc *_crtc) @@ -1721,10 +1725,10 @@ int bdw_enable_vblank(struct drm_crtc *_crtc) if (gen11_dsi_configure_te(crtc, true)) return 0; + spin_lock_irqsave(&display->irq.lock, irqflags); if (crtc->vblank_psr_notify && display->irq.vblank_enable_count++ == 0) schedule_work(&display->irq.vblank_notify_work); - spin_lock_irqsave(&display->irq.lock, irqflags); bdw_enable_pipe_irq(display, pipe, GEN8_PIPE_VBLANK); spin_unlock_irqrestore(&display->irq.lock, irqflags); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 197bdb6592a7..0496b3263ed1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1690,6 +1690,8 @@ struct intel_psr { bool pkg_c_latency_used; u8 active_non_psr_pipes; + + struct ref_tracker *vblank_wakeref; }; struct intel_dp { diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 0f791d0a5e0a..1eeae3243bd5 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -3982,14 +3982,20 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, bool enable) { struct intel_encoder *encoder; - bool block_dc_states = false; for_each_intel_encoder_with_psr(display->drm, encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); mutex_lock(&intel_dp->psr.lock); - if (CAN_PANEL_REPLAY(intel_dp)) - block_dc_states = true; + if (CAN_PANEL_REPLAY(intel_dp)) { + if (enable) + intel_dp->psr.vblank_wakeref = + intel_display_power_get(display, + POWER_DOMAIN_DC_OFF); + else + intel_display_power_put(display, POWER_DOMAIN_DC_OFF, + intel_dp->psr.vblank_wakeref); + } if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled && intel_dp->psr.pkg_c_latency_used) @@ -3997,18 +4003,6 @@ void intel_psr_notify_vblank_enable_disable(struct intel_display *display, mutex_unlock(&intel_dp->psr.lock); } - - /* - * NOTE: intel_display_power_set_target_dc_state is used - * only by PSR code for DC3CO handling. DC3CO target - * state is currently disabled in * PSR code. If DC3CO - * is taken into use we need take that into account here - * as well. - */ - if (block_dc_states) - intel_display_power_set_target_dc_state(display, enable ? - DC_STATE_DISABLE : - DC_STATE_EN_UPTO_DC6); } static void From 830c8a9b467e7d3a158483d37fa7dc13892b293a Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Fri, 5 Jun 2026 15:30:29 -0400 Subject: [PATCH 691/697] thunderbolt: property: Cap recursion depth in __tb_property_parse_dir() [ Upstream commit 928abe19fbf0127003abcb1ea69cabc1c897d0ab ] A DIRECTORY entry's value field is used as the dir_offset for a recursive call into __tb_property_parse_dir() with no depth counter. A crafted peer that chains DIRECTORY entries into a back-reference loop drives the parser until the kernel stack is exhausted and the guard page fires. Any untrusted XDomain peer (cable, dock, in-line inspector, adjacent host) that reaches the PROPERTIES_REQUEST control-plane exchange can trigger this without authentication. Thread a depth counter through tb_property_parse() and __tb_property_parse_dir(), and reject blocks that exceed TB_PROPERTY_MAX_DEPTH = 8. That is comfortably larger than any observed legitimate XDomain layout. Operators who do not need XDomain host-to-host discovery can disable the path entirely with thunderbolt.xdomain=0 on the kernel command line. Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-6 Assisted-by: Codex:gpt-5-4 Signed-off-by: Michael Bommarito Signed-off-by: Mika Westerberg Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/property.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index e3fdd086135b..bbd26d99c406 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -35,10 +35,11 @@ struct tb_property_dir_entry { }; #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 +#define TB_PROPERTY_MAX_DEPTH 8 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, size_t block_len, unsigned int dir_offset, size_t dir_len, - bool is_root); + bool is_root, unsigned int depth); static inline void parse_dwdata(void *dst, const void *src, size_t dwords) { @@ -97,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type) } static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, - const struct tb_property_entry *entry) + const struct tb_property_entry *entry, + unsigned int depth) { char key[TB_PROPERTY_KEY_SIZE + 1]; struct tb_property *property; @@ -118,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, switch (property->type) { case TB_PROPERTY_TYPE_DIRECTORY: dir = __tb_property_parse_dir(block, block_len, entry->value, - entry->length, false); + entry->length, false, depth + 1); if (!dir) { kfree(property); return NULL; @@ -163,13 +165,17 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, } static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, - size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) + size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root, + unsigned int depth) { const struct tb_property_entry *entries; size_t i, content_len, nentries; unsigned int content_offset; struct tb_property_dir *dir; + if (depth > TB_PROPERTY_MAX_DEPTH) + return NULL; + dir = kzalloc(sizeof(*dir), GFP_KERNEL); if (!dir) return NULL; @@ -200,7 +206,7 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, for (i = 0; i < nentries; i++) { struct tb_property *property; - property = tb_property_parse(block, block_len, &entries[i]); + property = tb_property_parse(block, block_len, &entries[i], depth); if (!property) { tb_property_free_dir(dir); return NULL; @@ -239,7 +245,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, return NULL; return __tb_property_parse_dir(block, block_len, 0, rootdir->length, - true); + true, 0); } /** From f54b30f3316a7d45be25a2abfb3359db89e78cd5 Mon Sep 17 00:00:00 2001 From: Michal Pecio Date: Mon, 18 May 2026 07:31:21 +0200 Subject: [PATCH 692/697] usb: core: Fix SuperSpeed root hub wMaxPacketSize commit d1e280334b7f0a1df441e08bd1f6a1bcc36b3bbb upstream. There is no good reason to have wBytesPerInterval < wMaxPacketSize - either one is too low or the other too high, and we may want to warn about such descriptors. Start with cleaning up our own root hubs. USB 3.2 section 10.15.1 sets wMaxPacketSize and wBytesPerInterval of SuperSpeed hub status endpoints at 2 bytes, so reduce wMaxPacketSize from its former value of 4, which was derived from USB 2.0 spec and the kernel's USB_MAXCHILDREN limit. They don't apply because USB 3.2 10.15.2.1 specifies SuperSpeed hubs to have up to 15 ports. Suggested-by: Mathias Nyman Signed-off-by: Michal Pecio Link: https://patch.msgid.link/20260518073121.7bc1da0f.michal.pecio@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 9dd79769cad1..e11a8818af74 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -332,9 +332,7 @@ static const u8 ss_rh_config_descriptor[] = { USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) - * see hub.c:hub_configure() for details. */ - (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */ 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ /* one SuperSpeed endpoint companion descriptor */ From adc6fc240a612611283cd537d15cb5cf0369a7e6 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Sun, 10 May 2026 12:29:01 -0700 Subject: [PATCH 693/697] tools: ynl: add scope qualifier for definitions commit fbf5df34a4dbcd09d433dd4f0916bf9b2ddb16de upstream. Using definitions in kernel policies is awkward right now. On one hand we want defines for max values and such. On the other we don't have a way of adding kernel-only defines. Adding unnecessary defines to uAPI is a bad idea, we won't be able to delete them. And when it comes to policy user space should just query it via the policy dump, not use hard coded defines. Add a "scope" property to definitions, which will let us tell the codegen that a definition is for kernel use only. Support following values: - uapi: render into the uAPI header (default, today's behavior) - kernel: render to kernel header only - user: same as kernel but for the user-side generated header Definitions may have a header property (definition is "external", provided by existing header). Extend the scope to headers, too. If definition has both scope and header properties we will only generate the includes in the right scope. Signed-off-by: Jakub Kicinski Link: https://patch.msgid.link/20260510192904.3987113-8-kuba@kernel.org Signed-off-by: Paolo Abeni Signed-off-by: Greg Kroah-Hartman --- Documentation/netlink/genetlink-c.yaml | 9 ++++++ Documentation/netlink/genetlink-legacy.yaml | 9 ++++++ Documentation/netlink/genetlink.yaml | 9 ++++++ Documentation/netlink/netlink-raw.yaml | 9 ++++++ tools/net/ynl/pyynl/ynl_gen_c.py | 31 +++++++++++++++++++-- 5 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Documentation/netlink/genetlink-c.yaml b/Documentation/netlink/genetlink-c.yaml index 5a234e9b5fa2..75fc40bd359a 100644 --- a/Documentation/netlink/genetlink-c.yaml +++ b/Documentation/netlink/genetlink-c.yaml @@ -69,6 +69,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags ] doc: diff --git a/Documentation/netlink/genetlink-legacy.yaml b/Documentation/netlink/genetlink-legacy.yaml index 66fb8653a344..f9c44747729a 100644 --- a/Documentation/netlink/genetlink-legacy.yaml +++ b/Documentation/netlink/genetlink-legacy.yaml @@ -83,6 +83,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags, struct ] # Trim doc: diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml index 7b1ec153e834..825d0e7e1ddd 100644 --- a/Documentation/netlink/genetlink.yaml +++ b/Documentation/netlink/genetlink.yaml @@ -55,6 +55,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags ] doc: diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml index 246fa07bccf6..f7c905c99936 100644 --- a/Documentation/netlink/netlink-raw.yaml +++ b/Documentation/netlink/netlink-raw.yaml @@ -81,6 +81,15 @@ properties: header: description: For C-compatible languages, header which already defines this value. type: string + scope: + description: | + Visibility of this definition. "uapi" (default) renders into + the uAPI header, "kernel" renders into the kernel-side + generated header, "user" renders into the user-side + generated header. When combined with `header:`, the + definition is not rendered, and the named header is + included only by code matching the scope. + enum: [ uapi, kernel, user ] type: enum: [ const, enum, flags, struct ] # Trim doc: diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py index aadeb3abcad8..14e3cf9acd53 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -3187,6 +3187,8 @@ def render_uapi(family, cw): for const in family['definitions']: if const.get('header'): continue + if const.get('scope', 'uapi') != 'uapi': + continue if const['type'] != 'const': cw.writes_defines(defines) @@ -3314,6 +3316,25 @@ def render_uapi(family, cw): cw.p(f'#endif /* {hdr_prot} */') +def render_scoped_consts(family, cw, scope): + defines = [] + for const in family['definitions']: + if const['type'] != 'const': + continue + if const.get('header'): + continue + if const.get('scope') != scope: + continue + name_pfx = const.get('name-prefix', f"{family.ident_name}-") + defines.append([ + c_upper(family.get('c-define-name', + f"{name_pfx}{const['name']}")), + const['value']]) + if defines: + cw.writes_defines(defines) + cw.nl() + + def _render_user_ntf_entry(ri, op): if not ri.family.is_classic(): ri.cw.block_start(line=f"[{op.enum_name}] = ") @@ -3474,8 +3495,12 @@ def main(): cw.p('#include "ynl.h"') headers = [] for definition in parsed['definitions'] + parsed['attribute-sets']: - if 'header' in definition: - headers.append(definition['header']) + if 'header' not in definition: + continue + scope = definition.get('scope', 'uapi') + if scope != 'uapi' and scope != args.mode: + continue + headers.append(definition['header']) if args.mode == 'user': headers.append(parsed.uapi_header) seen_header = [] @@ -3492,6 +3517,7 @@ def main(): for one in args.user_header: cw.p(f'#include "{one}"') else: + render_scoped_consts(parsed, cw, 'user') cw.p('struct ynl_sock;') cw.nl() render_user_family(parsed, cw, True) @@ -3499,6 +3525,7 @@ def main(): if args.mode == "kernel": if args.header: + render_scoped_consts(parsed, cw, 'kernel') for _, struct in sorted(parsed.pure_nested_structs.items()): if struct.request: cw.p('/* Common nested types */') From 2bbc395e81bd29c543a0529a678327e932a7ec69 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Mon, 1 Jun 2026 23:53:26 +0900 Subject: [PATCH 694/697] KVM: arm64: vgic-its: Drop the translation cache reference only for the erased entry commit 13031fb6b8357fbbcded2a7f4cba73e4781ee594 upstream. vgic_its_invalidate_cache() walks the per-ITS translation cache with xa_for_each() and drops the cache's reference on each entry with vgic_put_irq(). It puts the iterated pointer, though, rather than the value returned by xa_erase(). The function is called from contexts that do not exclude one another: the ITS command handlers hold its_lock, the GITS_CTLR write path holds cmd_lock, and the path that clears EnableLPIs in a redistributor's GICR_CTLR holds neither. Two or more of them can drain the same cache concurrently, and if each one observes the same entry, erases it and then puts it, the single reference the cache holds on that entry is dropped more than once. The entry can then be freed while an ITE still maps it. xa_erase() is atomic and returns the previous entry, so put only the entry that this context actually removed. The cache reference is then dropped exactly once per entry even when the invalidations run concurrently, and the behavior is unchanged when only one context runs. Fixes: 8201d1028caa ("KVM: arm64: vgic-its: Maintain a translation cache per ITS") Signed-off-by: Hyunwoo Kim Reviewed-by: Oliver Upton Link: https://patch.msgid.link/ah2c5lu4JbUg7dj-@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/vgic/vgic-its.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c index a4c0fb7f02ea..fa58668bcf1b 100644 --- a/arch/arm64/kvm/vgic/vgic-its.c +++ b/arch/arm64/kvm/vgic/vgic-its.c @@ -597,8 +597,10 @@ static void vgic_its_invalidate_cache(struct vgic_its *its) unsigned long idx; xa_for_each(&its->translation_cache, idx, irq) { - xa_erase(&its->translation_cache, idx); - vgic_put_irq(kvm, irq); + /* Only the context that erases the entry drops its cache ref. */ + irq = xa_erase(&its->translation_cache, idx); + if (irq) + vgic_put_irq(kvm, irq); } } From 918450ad6010df6ecd2efde12a1409e011da22d6 Mon Sep 17 00:00:00 2001 From: Hyunwoo Kim Date: Fri, 5 Jun 2026 17:27:01 +0900 Subject: [PATCH 695/697] KVM: arm64: Reassign nested_mmus array behind mmu_lock commit 70543358fa08e0f7cebc3447c3b70fe97ad7aaa8 upstream. kvm->arch.nested_mmus[] is walked under kvm->mmu_lock, including from the MMU notifier path (kvm_unmap_gfn_range() -> kvm_nested_s2_unmap()), which can run at any time. kvm_vcpu_init_nested() reallocates the array and frees the old buffer while holding only kvm->arch.config_lock, so such a walker can reference the freed array. Allocate the new array outside of mmu_lock, as the allocation can sleep. Under the lock, copy the existing entries, fix up the back pointers and reassign the array. Free the old buffer after dropping the lock, as kvfree() can sleep as well. Fixes: 4f128f8e1aaac ("KVM: arm64: nv: Support multiple nested Stage-2 mmu structures") Signed-off-by: Hyunwoo Kim Reviewed-by: Oliver Upton Link: https://patch.msgid.link/aiKIVVeIr1aAB1yp@v4bel Signed-off-by: Marc Zyngier Cc: stable@vger,kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/nested.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c index f04cda40545b..df956559fd06 100644 --- a/arch/arm64/kvm/nested.c +++ b/arch/arm64/kvm/nested.c @@ -89,21 +89,28 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu) * again, and there is no reason to affect the whole VM for this. */ num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU; - tmp = kvrealloc(kvm->arch.nested_mmus, - size_mul(sizeof(*kvm->arch.nested_mmus), num_mmus), - GFP_KERNEL_ACCOUNT | __GFP_ZERO); - if (!tmp) - return -ENOMEM; - swap(kvm->arch.nested_mmus, tmp); + if (num_mmus > kvm->arch.nested_mmus_size) { + tmp = kvcalloc(num_mmus, sizeof(*tmp), GFP_KERNEL_ACCOUNT); + if (!tmp) + return -ENOMEM; - /* - * If we went through a realocation, adjust the MMU back-pointers in - * the previously initialised kvm_pgtable structures. - */ - if (kvm->arch.nested_mmus != tmp) - for (int i = 0; i < kvm->arch.nested_mmus_size; i++) - kvm->arch.nested_mmus[i].pgt->mmu = &kvm->arch.nested_mmus[i]; + write_lock(&kvm->mmu_lock); + + if (kvm->arch.nested_mmus_size) { + memcpy(tmp, kvm->arch.nested_mmus, + size_mul(sizeof(*tmp), kvm->arch.nested_mmus_size)); + + for (int i = 0; i < kvm->arch.nested_mmus_size; i++) + tmp[i].pgt->mmu = &tmp[i]; + } + + swap(kvm->arch.nested_mmus, tmp); + + write_unlock(&kvm->mmu_lock); + + kvfree(tmp); + } for (int i = kvm->arch.nested_mmus_size; !ret && i < num_mmus; i++) ret = init_nested_s2_mmu(kvm, &kvm->arch.nested_mmus[i]); From acb7cf4c1184e27622be0faf89244d5001ed1e87 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 9 Jun 2026 12:28:53 +0200 Subject: [PATCH 696/697] Linux 6.18.35 Link: https://lore.kernel.org/r/20260607095727.528828913@linuxfoundation.org Tested-by: Miguel Ojeda Tested-by: Peter Schneider Tested-by: Wentao Guan Tested-by: Ron Economos Tested-by: Florian Fainelli Signed-off-by: Greg Kroah-Hartman --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5087bd6183dd..0b24b388e16c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 6 PATCHLEVEL = 18 -SUBLEVEL = 34 +SUBLEVEL = 35 EXTRAVERSION = NAME = Baby Opossum Posse From 25c7c4084aff013608a1fc21d14ae9444c6ef35c Mon Sep 17 00:00:00 2001 From: Ameer Hamza Date: Wed, 10 Jun 2026 15:09:15 +0500 Subject: [PATCH 697/697] Bump changelog for 6.18.35+truenas Signed-off-by: Ameer Hamza --- scripts/package/truenas/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/package/truenas/changelog b/scripts/package/truenas/changelog index 64ab663357ee..ae9221e4e9c8 100644 --- a/scripts/package/truenas/changelog +++ b/scripts/package/truenas/changelog @@ -1,3 +1,9 @@ +linux-6.18.35+truenas (6.18.35+truenas-1) sid; urgency=low + + * Merge upstream v6.18.35 release. + + -- Debian Packages List Wed, 10 Jun 2026 04:00:00 +0500 + linux-6.18.33+truenas (6.18.33+truenas-1) sid; urgency=low * Merge upstream v6.18.33 release.